pax_global_header00006660000000000000000000000064130455213210014506gustar00rootroot0000000000000052 comment=e5c2d145d020e080c8612f80a3d123f3a024a13a .gitignore000066400000000000000000000005151304552132100130430ustar00rootroot00000000000000Makefile.in configure *.o INSTALL Makefile aclocal.m4 config.guess config.sub config.log config.status install-sh missing mkinstalldirs depcomp gobby-0.5 autom4te.cache .deps compile prjwc prjtodo intltool-extract.in intltool-merge.in intltool-update.in gnome-doc-utils.make gobby-0.5.desktop *.gschema.xml *.gschema.valid .dirstamp .travis.yml000066400000000000000000000016171304552132100131700ustar00rootroot00000000000000language: cpp sudo: required dist: trusty compiler: - gcc - clang before_install: - which $CC - export CC=`which $CC` - sudo apt-get update -qq - sudo apt-get install -qq gtk-doc-tools libgnutls28-dev libgsasl7-dev libxml2-dev libglib2.0-dev libgtk-3-dev libavahi-client-dev libdaemon-dev libpam-dev gobject-introspection libexpat1-dev zlib1g-dev - wget https://github.com/gobby/libinfinity/archive/master.tar.gz -O /tmp/infinote-master.tar.gz - tar -xzf /tmp/infinote-master.tar.gz - cd libinfinity-master && ./autogen.sh --with-gtk3 --with-infgtk --with-inftextgtk --with-avahi --with-libdaemon --disable-gtk-doc && make -j3 && sudo -E make install && cd .. install: - sudo apt-get install -qq libxml++2.6-dev libglibmm-2.4-dev libgtkmm-3.0-dev libgtksourceview-3.0-dev libgsasl7-dev libunique-3.0-dev gettext intltool gnome-doc-utils rarian-compat script: ./autogen.sh && ./configure && make -j3 AUTHORS000066400000000000000000000023471304552132100121300ustar00rootroot00000000000000gobby ===== Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. Copyright (c) 2008-2014, Armin Burgmeier Authors: * Armin Burgmeier * Philipp Kern Contributors: * Benjamin Herr * Ben Levitt * Gabríel A. Pétursson Translations: French: * Peer Janssen * Mohammed Adnene Trojette Spanish: * Mario Palomo Swedish: * Daniel Nylander German: * Philipp Kern COPYING000066400000000000000000000014031304552132100121030ustar00rootroot00000000000000ISC License Copyright (c) 2008-2014, Armin Burgmeier Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ChangeLog.manual000066400000000000000000003170001304552132100141010ustar00rootroot000000000000002012-12-04 Philipp Kern * po/LINGUAS: * po/ca.po: Add Catalan translation, thanks to Jordi Mallach. 2012-04-30 Philipp Kern * gobby-0.5.1: Fix a grammar mistake. 2012-04-29 Armin Burgmeier * code/commands/autosave-commands.cpp: * code/commands/folder-commands.cpp: * code/commands/user-join-commands.cpp: * code/core/filechooser.cpp: * code/core/folder.cpp: * code/core/header.cpp: * code/core/preferences.cpp: * code/core/statusbar.cpp: * code/core/textsessionview.cpp: * code/dialogs/initial-dialog.cpp: * code/dialogs/preferences-dialog.cpp: * code/operations/operation-open.cpp: * code/operations/operation-open.hpp: * code/operations/operation-save.hpp: Add some missing glibmm includes that are no longer pulled in by gtkmm since recent gtkmm3 versions. 2012-03-18 Philipp Kern * code/util/gtk-compat.hpp: Fix the call of Gtk3's gtk-widget-render-icon-pixbuf, which is allowed to return NULL if the stock icon does not exist. Guard it with MISSING_IMAGE. 2012-03-18 Philipp Kern * configure.ac: Support the new libinfinity library names built against Gtk3. 2011-10-25 Philipp Kern * configure.ac: drop AM_MAINTAINER_MODE 2011-08-07 Philipp Kern * code/core/browser.cpp: * code/core/browser.hpp: * code/util/historyentry.cpp: * code/util/historyentry.hpp: Make direct connection host name entry field accessible. (#570) 2011-07-30 Philipp Kern * code/core/documentinfostorage.cpp: * code/core/folder.cpp: * code/util/config.cpp: * code/util/file.cpp: create directories with mode 0700 (Debian bug #636015) 2011-04-03 Philipp Kern * *.cpp, *.hpp: adjusted the address of the Free Software Foundation to match the current one 2011-03-27 Armin Burgmeier * configure.ac: Post-release bump to 0.4.95, require libinfinity-0.6. * README: Update dependencies list. 2011-03-27 Armin Burgmeier * === Released 0.4.94 === * README: * NEWS: Update for release. 2011-03-27 Armin Burgmeier * configure.ac: Use unique-3.0 when compiling against GTK+ 3.0. 2011-01-30 Armin Burgmeier * code/commands/subscription-commands.cpp (on_notify_connection): Don't show the info text if session's state is not RUNNING. In that case the connection was unset in SYNCHRONIZING state in which case synchronization-commands will set a more specific error message. 2011-01-30 Armin Burgmeier * code/core/folder.cpp (remove_document): Don't call inf_session_close() if the session is already closed. This can happen if the session synchronization fails. 2011-01-29 Armin Burgmeier * all source files: Add 2011 to copyright years. 2011-01-29 Armin Burgmeier * code/core/textsessionview.cpp (get_cursor_position): Fix a compiler warning. 2011-01-29 Armin Burgmeier * code/core/textsessionuserview.hpp: * code/core/textsessionuserview.cpp: * code/core/Makefile.am: Add the InfTextSessionUserView class which handles scrolling to a remote user's cursor when double-clicking it in the user list. * code/core/userlist.hpp: * code/core/userlist.cpp: Add the user_activated signal. * code/core/folder.cpp: Create a TextSessionUserView for text sessions. 2011-01-29 Armin Burgmeier * code/core/sessionuserview.hpp: Fix the include guard. 2011-01-29 Armin Burgmeier * code/util/gtk-compat.hpp: Add a wrapper for Gtk::IconSet which is now used by a RefPtr in gtkmm 3. * code/core/iconmanager.hpp: * code/core/iconmanager.cpp: Adapt for that. * code/core/textsessionview.hpp: * code/operations/operations.hpp: * code/operations/operation-export-html.cpp: Rearrange some include directives to avoid including gtkmm headers after Xlib headers, which causes trouble because of common Xlib #defines such as None, Window or Status. 2011-01-24 Armin Burgmeier * code/util/gtk-compat.cpp: Fix a crash when connecting to a server with GTK+ 2. 2011-01-23 Armin Burgmeier * code/util/gtk-compat.hpp: * code/util/historyentry.hpp: * code/util/historyentry.cpp: * code/util/Makefile.am: * code/core/browser.cpp: * code/core/closableframe.cpp: * code/core/folder.hpp: * code/core/folder.cpp: * code/core/preferences.cpp: * code/core/sessionuserview.cpp: * code/core/statusbar.cpp: * code/core/tablabel.cpp: * code/core/texttablabel.hpp: * code/core/texttablabel.cpp: * code/core/userlist.cpp: * code/dialogs/document-location-dialog.cpp: * code/dialogs/entry-dialog.cpp: * code/dialogs/find-dialog.cpp: * code/dialogs/goto-dialog.cpp: * code/dialogs/initial-dialog.cpp: * code/dialogs/open-location-dialog.cpp: * code/dialogs/preferences-dialog.cpp: * code/operations/operation-export-html.cpp: * code/operations/operation-save.cpp: * code/commands/browser-context-commands.cpp: * code/commands/edit-commands.cpp: * code/commands/help-commands.cpp: * code/commands/user-join-commands.cpp: * code/commands/view-commands.cpp: * code/window.cpp: * code/main.cpp: * configure.ac: Add a --with-gtk3 switch to Gobby and adapt the code so that it compiles with gtkmm-3. If GTK 3 is enabled then the --with-unique switch is ignored. Eventually we need to port that code to GtkApplication. 2010-12-25 Armin Burgmeier * code/core/browser.hpp: * code/core/browser.cpp: * code/commands/auth-commands.hpp: * code/commands/auth-commands.cpp: Adapt to latest API changes in libinfinity, show password dialog asynchronously. 2010-09-21 Armin Burgmeier * code/commands/browser-context-commands.hpp: * code/commands/browser-context-commands.cpp: Add a "Disconnect from Server" option to the context menu (#542). 2010-09-21 Armin Burgmeier * code/core/preferences.hpp: * code/core/preferences.cpp: Add preferences options for showing remote cursors, selections, current lines and cursor positions in the scrollbar. * code/core/textsessionview.hpp: * code/core/textsessionview.cpp: Apply the new settings on the underlying objects. * code/dialogs/preferences-dialog.hpp: * code/dialogs/preferences-dialog.cpp: Add the new options to the preferences dialog. 2010-06-04 Armin Burgmeier * code/core/texttablabel.hpp: * code/core/texttablabel.cpp: Adapt for a recent API change in InfTextBuffer. 2010-03-02 Armin Burgmeier * gobby-0.5.desktop.in: Added GenericName and X-GNOME-FullName fields, changed Name to be just Gobby. Bug #528. 2010-02-25 Armin Burgmeier * code/core/textsessionview.hpp: * code/core/textsessionview.cpp: Show remote users' position in scrollbar using InfTextGtkViewport. 2010-02-14 Benjamin Herr * code/dialogs/password-dialog.cpp: * code/operations/operation-delete.cpp: * code/operations/operation-new.cpp: * code/operations/operation-open.cpp: * code/operations/operation-save.cpp: Added quotation marks in messages including file paths or document names. * code/operations/operation-export-html.cpp: Quotations marks as above, also moved a comment so it shows up in .po files. * code/commands/help-commands.cpp: Updated FSF address in GPL, added Michael Frey to translation credits. 2010-02-13 Armin Burgmeier * code/core/textsessionview.hpp: * code/core/textsessionview.cpp: Add a InfTextGtkView, to show remote cursors. 2010-02-12 Armin Burgmeier * all source files: Added 2010 to copyright years. 2010-02-09 Benjamin Herr * code/commands/auth-commands.hpp: * code/commands/auth-commands.cpp: Handle _TRY_AGAIN errors, do the whole retrying thing properly. 2010-01-31 Benjamin Herr , Armin Burgmeier * code/commands/auth-commands.cpp: Adapt to the removal of the user-authenticated signal, instead query detailed SASL error using inf_xmpp_connection_get_sasl_error(). 2009-01-27 Benjamin Herr * code/commands/auth-commands.cpp: Display errors from the InfPostAuthenticationError domain properly instead of a generic "unknown error". 2009-12-23 Armin Burgmeier * code/core/folder.hpp: * code/core/folder.cpp: Added get_document(). * code/commands/file-tasks/task-save-all.cpp (run): Use it, instead of (wrongly) iterating the folder's documents by itself, fixing "Save All". Bug #519. 2009-12-19 Armin Burgmeier * configure.ac: Post-release bump to 0.4.94, require libinfinity-0.5. * README: Update dependencies list. 2009-12-19 Armin Burgmeier * === Released 0.4.93 === * README: * NEWS: Update for release. 2009-12-19 Armin Burgmeier * code/core/browser.cpp (compare_func): Don't use result when it is uninitialized. 2009-12-19 Armin Burgmeier * configure.ac: * Makefile.am: Don't require gnome-doc-utils do be available, build without documentation if they are not, such as on Windows. 2009-12-19 Armin Burgmeier * code/dialogs/preferences-dialog.cpp: Don't add GTK_SOURCE_DRAW_SPACES_NBSP when showing flags since this does not exist in GtkSourceView 2.4.0 yet (which we require). We may it add later when we bump the GtkSourceView dependency. 2009-12-19 Armin Burgmeier * code/dialogs/password-dialog.hpp: * code/dialogs/connection-dialog.hpp: Include , to fix the build with gtkmm 2.12. 2009-12-19 Armin Burgmeier * code/core/browser.cpp: Sort top-level items by name. 2009-12-18 Armin Burgmeier * help/C/gobby.xml: Mention Shift+Ctrl+C shortcut for color reset. 2009-12-18 Armin Burgmeier * code/core/header.hpp: * code/core/header.cpp: Added a reset user color item to the view menu. * code/commands/view-commands.hpp: * code/commands/view-commands.cpp: Added corresponding functionality, using inf_text_gtk_buffer_show_user_colors(). Bug #211. 2009-12-16 Armin Burgmeier * Makefile.am: Added gobby-0.5.desktop.in to EXTRA_DIST. * help/C/gobby.xml: Added dummy items for referenced sections, so that the XML validates. This fixes make distcheck. 2009-12-16 Armin Burgmeier * Makefile.am: Don't install the .desktop file on Windows. 2009-12-15 Armin Burgmeier * code/dialogs/password-dialog.cpp: Fix spacings. 2009-12-15 Benjamin Herr * code/commands/file-commands.hpp: * code/commands/file-commands.cpp: * code/core/header.hpp: * code/core/header.cpp: * code/dialogs/Makefile.am: Added a File menu action to connect to a server, equivalent to the existing Direct Connection entry. 2009-12-15 Benjamin Herr * code/commands/Makefile.am: * code/commands/auth-commands.hpp: * code/commands/auth-commands.cpp: * code/core/browser.hpp: * code/core/browser.cpp: * code/dialogs/Makefile.am: * code/dialogs/password-dialog.hpp: * code/dialogs/password-dialog.cpp: * code/window.hpp: * code/window.cpp: Added support for SASL PLAIN authentication, prompting the user for a password upon connection. 2009-12-15 Benjamin Herr * configure.ac: Enable optional AM_SILENT_RULES. 2009-12-14 Armin Burgmeier * README: Update IRC channel, updated dependencies. 2009-12-14 Armin Burgmeier * code/commands/browser-context-commands.cpp (on_populate_popup): Don't crash when right-clicking on a browser item whose connection is currently being established. 2009-12-14 Armin Burgmeier * code/commands/browser-commands.cpp (on_notify_status): Close network connection when server does not send initial welcome message. 2009-12-10 Benjamin Herr * code/commands/browser-context-commands.cpp: Do not try to create context menu popup for directories that are not in working order. Fixes a crash when rightclicking disconnected browser entries. 2009-12-10 Benjamin Herr * code/core/browser.cpp: Use new inf_tcp_connection_new() function instead of manually creating a GObject. 2009-12-07 Armin Burgmeier * code/commands/browser-commands.hpp: * code/commands/browser-commands.cpp: Simplified the code a bit using the new InfcBrowser "status" property. 2009-12-05 Armin Burgmeier * code/core/textundogrouping.hpp: * code/core/textundogrouping.cpp: * code/core/Makefile.am: Added a thin wrapper around InfTextUndoGrouping which also encloses GtkTextBuffer's user actions into an undo group. * code/core/textsessionview.hpp: * code/core/textsessionview.cpp: Instantiate an undo group when an active user is set. * code/commands/edit-commands.cpp: Undo multiple requests according to the document's undo grouping. 2009-12-03 Gabríel A. Pétursson * code/commands/help-commands.cpp: Added translator credits. 2009-12-03 Gabríel A. Pétursson * code/dialogs/find-dialog.cpp: Fixed a spelling error. 2009-12-02 Gabríel A. Pétursson * code/window.cpp: * code/core/statusbar.hpp: * code/core/statusbar.cpp: Do not show the statusbar's grip handle while the window is maximized. 2009-12-02 Armin Burgmeier * code/commands/edit-commands.cpp: Adapt to another libinfinity API change: inf_adopted_session_undo() and inf_adopted_session_redo() have a new guint parameter. 2009-12-01 Armin Burgmeier * code/core/chattablabel.hpp: * code/core/chattablabel.cpp: * code/core/Makefile.am: Added a tab label for the chat tabs. * code/core/folder.cpp: Make use of it for new chat sessions. 2009-12-01 Armin Burgmeier * code/core/tablabel.hpp: * code/core/tablabel.cpp: Removed all text-specific functionality from TabLabel. * code/core/texttablabel.hpp: * code/core/texttablabel.cpp: * code/core/Makefile.am: Readded it to TextTagTabel which derives from TabLabel. * code/core/folder.cpp: Create a TextTabLabel for text documents. 2009-12-01 Gabríel A. Pétursson * code/core/statusbar.cpp: Do not display documents' status on the status bar when there are no documents open. 2009-12-01 Gabríel A. Pétursson * code/core/statusbar.hpp: * code/core/statusbar.cpp: Display the overwrite status on the status bar. 2009-11-30 Armin Burgmeier * code/core/noteplugin.cpp: * code/operations/operation-open.cpp: Adapt to recent API change in libinfinity: inf_text_session_new() and InfcNotePlugin's session_new() now take a InfSessionStatus parameter. 2009-11-30 Armin Burgmeier * code/commands/folder-commands.cpp (constructor): Fixed a comparison of signed vs. unsigned compiler warning. 2009-11-30 Armin Burgmeier * code/commands/subscription-commands.cpp (on_notify_connection): Also reset the active user for chat sessions. This prevents a crash when changing to a chat session tab which belongs to a closed chat session. 2009-11-21 Armin Burgmeier * code/core/sessionview.hpp: * code/core/sessionview.cpp: * code/core/chatsessionview.hpp: * code/core/chatsessionview.cpp: * code/core/textsessionview.hpp: * code/core/textsessionview.cpp: Moved get_active_user and the active-user-changed signal to the base class. * code/commands/folder-commands.hpp: * code/commands/folder-commands.cpp: Make this work with chat sessions. * code/dialogs/find-dialog.hpp: * code/dialogs/find-dialog.cpp: * code/commands/edit-commands.hpp: * code/commands/edit-commands.cpp: Adapt for changed active-user-changed signal signature. * code/window.hpp: * code/window.cpp: Renamed commands member variables to have _commands as suffix, not as prefix, and added a folder commands for the chat folder. 2009-11-21 Philipp Kern * help/C/gobby.xml: document Editor settings a bit more 2009-11-21 Philipp Kern * code/dialogs/preferences-dialog.cpp: fix en_UK-ism 2009-11-21 Philipp Kern * help/C/gobby.xml: add shortcuts 2009-11-17 Philipp Kern * help/C/gobby.xml: point to the new IRC channel on Freenode 2009-11-17 Armin Burgmeier * code/window.hpp: * code/window.cpp: Switch to text when chat pane is hidden while it had focus, and give it focus if it is shown and nothing else has focus already. 2009-11-17 Armin Burgmeier * code/core/chatsessionview.hpp: Added get_chat(). * code/window.hpp: * code/window.cpp: Added Ctrl+M and Ctrl+Shift+M shortcuts to easily jump between chat and document. 2009-11-17 Armin Burgmeier * code/core/closableframe.hpp: * code/core/closableframe.cpp: Add set_allow_visible(). * code/core/preferences.hpp: * code/core/preferences.cpp: Added settings for chat visibility and chat userlist visibility and width. * code/core/folder.cpp: Use different preference settings for document userlist visibility and chat userlist visibility. * code/core/iconmanager.hpp: * code/core/iconmanager.cpp: Added stock chat icon. * code/core/header.hpp: * code/core/header.cpp: Added "View Chat" and "View Chat User List" menu items and actions. * code/commands/view-commands.hpp: * code/commands/view-commands.cpp: Implemented functionality for the new actions. * code/window.hpp: * code/window.cpp: Pass chat frame and folder to view-commands. 2009-11-17 Armin Burgmeier * code/util/color.hpp: * code/util/color.cpp: Fixed return type of rgb_to_hsv() and hsv_to_rgb(). 2009-11-15 Armin Burgmeier * code/core/userlist.hpp: * code/core/userlist.cpp: Added set_show_disconnected(). * code/core/sessionuserview.hpp: * code/core/sessionuserview.cpp: Added a show_disconnected parameter in the constructor. * code/core/folder.cpp: Don't show disconnected users for chat sessions. 2009-11-15 Gabríel A. Pétursson * code/commands/help-commands.cpp: Updated the about dialog. 2009-11-11 Benjamin Herr * code/core/textsessionview.hpp: * code/core/textsessionview.bpp: On style change, use new libinfinity functionality to adjust user colour saturation/value. The formulas used might not be optimal, so there is room for improvement. 2009-11-10 Benjamin Herr * code/core/statusbar.cpp: Manually add a "Close" button to the status bar message dialog because RESPONSE_CLOSE seems to add a "Cancel" button instead for some people (Gabríel). 2009-11-10 Gabríel A. Pétursson * code/commands/browser-context-commands.hpp: * code/commands/browser-context-commands.cpp: Fixed an assertion error. 2009-11-09 Benjamin Herr , Armin Burgmeier * code/core/statusbar.hpp: * code/core/statusbar.cpp: Readded timeout mechanism into status bar message functions. * code/dialogs/find-dialog.cpp: Readded timeout for status bar messages so that they would actually disappear at some point. 2009-11-09 Benjamin Herr , Armin Burgmeier * code/core/statusbar.cpp: Changed "OK" back to "Close" in status bar error message popup dialog. 2009-11-09 Benjamin Herr , Armin Burgmeier * code/core/statusbar.cpp: Made status bar error message popup dialog non-modal. 2009-11-09 Gabríel A. Pétursson , Armin Burgmeier * code/core/preferences.hpp: * code/core/preferences.cpp: Added scheme_id preference option to appearance. * code/core/textsessionview.hpp: * code/core/textsessionview.cpp: Set GtkSourceView style scheme according to selected scheme ID. * code/dialogs/preferences-dialog.hpp: * code/dialogs/preferences-dialog.cpp: Add a list to choose one's favoured style scheme to the appearance tab. 2009-11-09 Benjamin Herr * code/core/statusbar.cpp: * code/core/statusbar.hpp: Add hide_message that only removes a message widget without removing the (then-null) pointer from the m_list. hide_message can be called multiple times, thus avoiding the crash when an info message was removed twice. 2009-11-09 Benjamin Herr * code/core/statusbar.cpp: * code/core/statusbar.hpp: Minor refactoring to the status bar message change. Also made more stuff a detail of StatusBar::Message. 2009-11-08 Armin Burgmeier * code/commands/browser-commands.hpp: * code/commands/browser-commands.cpp: Initiate a subscription to a server's chat upon connection. * code/commands/subscription-commands.hpp: * code/commands/subscription-commands.cpp: Create a ChatSessionView in the bottom pane in case of a chat subscription. * code/commands/user-join-commands.cpp: Do a chat user join for chat sessions. * code/core/folder.hpp: * code/core/folder.cpp: Added a hide_single_tab option. * code/window.cpp: Adapt to changed folder and browser-commands constructors. 2009-11-08 Armin Burgmeier * code/commands/subscription-commands.hpp: * code/commands/subscription-commands.cpp: New class handling session subscriptions. * code/commands/synchronization-commands.hpp: * code/commands/synchronization-commands.cpp: New class handling session synchronization. * code/commands/user-join-commands.hpp: * code/commands/user-join-commands.cpp: New class handling user join. * code/commands/browser-commands.hpp: * code/commands/browser-commands.cpp: Removed the functionality that has been moved to the classes mentioned above. * code/commands/Makfile.am: Add the new files to the build. * code/window.hpp: * code/window.cpp: Instantiante the new classes. 2009-11-08 Armin Burgmeier * code/core/chatsessionview.hpp: * code/core/chatsessionview.cpp: Added a SessionView-deriving class to show a chat session. * code/core/Makefile.am: Added the new files to the build. * code/core/folder.hpp: * code/core/folder.cpp: Added add_chat_session, renamed add_document to add_text_session. * code/commands/browser-commands.cpp: Use add_text_session instead of add_document. * code/window.hpp: * code/window.cpp: Added a bottom pane for the chat. 2009-10-25 Armin Burgmeier * code/core/textsessionview.hpp: * code/core/textsessionview.cpp: Added TextSessionView, a SessionView-deriving class showing a InfTextSession. * code/core/docwindow.hpp: * code/core/docwindow.cpp: Removed. * code/core/Makefile.am: Adapted the build. * code/core/sessionview.hpp: * code/core/sessionview.cpp: Added path & hostname to SessionView. * code/core/folder.hpp: * code/core/folder.cpp: * code/core/statusbar.hpp: * code/core/statusbar.cpp: * code/core/tablabel.hpp: * code/core/tablabel.cpp: * code/core/titlebar.hpp: * code/core/titlebar.cpp: * code/core/userlist.cpp: * code/dialogs/find-dialog.hpp: * code/dialogs/find-dialog.cpp: * code/dialogs/goto-dialog.hpp: * code/dialogs/goto-dialog.cpp: * code/operations/operation-export-html.hpp: * code/operations/operation-export-html.cpp: * code/operations/operation-save.hpp: * code/operations/operation-save.cpp: * code/operations/operations.hpp: * code/operations/operations.cpp: * code/commands/autosave-commands.hpp: * code/commands/autosave-commands.cpp: * code/commands/browser-commands.cpp: * code/commands/edit-commands.hpp: * code/commands/edit-commands.cpp: * code/commands/file-commands.hpp: * code/commands/file-commands.cpp: * code/commands/folder-commands.hpp: * code/commands/folder-commands.cpp: * code/commands/view-commands.hpp: * code/commands/view-commands.cpp: * code/commands/file-tasks/task-export-html.hpp: * code/commands/file-tasks/task-export-html.cpp: * code/commands/file-tasks/task-save-all.hpp: * code/commands/file-tasks/task-save-all.cpp: * code/commands/file-tasks/task-save.hpp: * code/commands/file-tasks/task-save.cpp: * code/window.cpp: Adapt for the DocWindow->TextSessionView API change. 2009-10-18 Armin Burgmeier * code/core/sessionview.hpp: * code/core/sessionview.cpp: Added SessionView base class which will replace DocWindow soon. * code/core/sessionuserview.hpp: * code/core/sessionuserview.cpp: Added SessionUserView, a wrapper around SessionView with a userlist next to it. * code/core/Makefile.am: Added the new files to the build. 2009-10-23 Armin Burgmeier * code/core/userlist.hpp: * code/core/userlist.cpp: Made UserList work with non-InfText users. * code/core/docwindow.cpp: Only pass InfUserTable to the userlist, not the whole session. 2009-11-09 Gabríel A. Pétursson * code/commands/browser-commands.cpp: * code/commands/file-tasks/task-open.cpp: * code/core/browser.cpp: * code/core/statusbar.cpp: * code/core/statusbar.hpp: * code/dialogs/find-dialog.cpp: * code/operations/operation-delete.cpp: * code/operations/operation-export-html.cpp: * code/operations/operation-new.cpp: * code/operations/operation-open-multiple.cpp: * code/operations/operation-open.cpp: * code/operations/operation-save.cpp: Minor refactoring. Added detailed status bar error messages. 2009-11-07 Armin Burgmeier * code/commands/browser-context-commands.cpp (on_open_response): Use OperationOpenMultiple instead of mulitple OperationOpens, so that the documents are opened one after the other, not all at once. 2009-11-07 Gabríel A. Pétursson * code/commands/browser-context-commands.cpp: * code/commands/file-tasks/task-open-file.hpp: * code/commands/file-tasks/task-open-file.cpp: Added the ability to upload multiple files simultaneously. 2009-11-07 Armin Burgmeier * AUTHORS: Updated. 2009-11-07 Gabríel A. Pétursson * code/commands/file-commands.cpp: * code/core/browser.hpp: * code/core/browser.cpp: Alphabetical sorting of directory and file names. 2009-11-06 Armin Burgmeier * code/commands/folder-commands.cpp: Avoid a crash if the connection goes down before the activation timeout fired, i.e. right after changing a tab. 2009-11-01 Armin Burgmeier * configure.ac: Require glibmm-2.4 >= 2.18.0 and giomm-2.4 >= 2.18.0 to make sure overwriting files works when saving. See also bug #471. 2009-11-01 Benjamin Herr * code/core/preferences.hpp: * code/core/preferences.cpp: Added view.whitespace_display option. * code/dialogs/preferences-dialog.hpp: * code/dialogs/preferences-dialog.cpp: Added a corresponding widget to the View pane in the preferences dialog. * code/core/docwindow.hpp: * code/core/docwindow.cpp: Honor the setting. * configure.ac: Require gtksourceview-2.0 >= 2.4 since gtk_source_view_set_draw_spaces() is available only since this version. 2009-10-31 Armin Burgmeier * code/Makefile.am: Pass both public and private icon paths to the code. * code/core/iconmanager.cpp: Add both to the icon search path. This fixes lookup of the application icon if gobby is installed in a non-standard prefix. 2009-10-26 Armin Burgmeier * code/core/userlist.cpp (sort_func): Sort the user list case-insensitively. 2009-10-26 Armin Burgmeier * code/operations/operation-open.cpp (constructor): Also disable GtkSourceView's Undo for sync'ed-in sessions. Otherwise the user was able to press Ctrl+Z in a newly opened document to trigger GtkSourceView (global) Undo when local group Undo is not available (such as right after document creation). 2009-10-26 Armin Burgmeier * code/commands/browser-commands.cpp (on_subscribe_session): Use the remote-hostname of the InfXmppConnection of the browser instead of the root entry name of the browser model as hostname passed to the DocWindow. This is currently used in the exported HTML's footer. 2009-10-25 Armin Burgmeier , Benjamin Herr * code/operations/operation-export-html.hpp: * code/operations/operation-export-html.cpp: * code/operations/Makefile.am: Added an operation which exports a document to a XHTML document. * code/operations/operations.hpp: * code/operations/operations.cpp: Added export_html() functio. * code/core/header.hpp: * code/core/header.cpp: Added a menu entry for exporting a document to HTML. * code/core/folder.hpp: * code/core/folder.cpp: * code/core/docwindow.hpp: * code/core/docwindow.cpp: * code/commands/browser-commands.cpp: Store path on remote host and its hostname in the DocWindow. * code/commands/file-tasks/task-export-html.hpp: * code/commands/file-tasks/task-export-html.cpp: * code/commands/file-tasks/Makefile.am: Added a task for HTML export showing a file chooser dialog for the user to select a location to export to. * code/commands/file-commands.hpp: * code/commands/file-commands.cpp: Start the task when the user activates the corresponding action defined in Header. 2009-10-24 Armin Burgmeier * icons/hicolor/scalable/status/user-color-indicator.svg: Removed blur, so that rsvg renders it correctly. * icons/hicolor/scalable/Makefile.am: * icons/hicolor/48x48/Makefile.am: * icons/hicolor/48x48/status/Makefile.am: * icons/hicolor/scalable/status/Makefile.am: * configure.ac: Added the new item to the build system. * code/util/color.hpp: * code/util/color.cpp: Added public rgb_to_hsv() and hsv_to_rgb() functions. * code/core/iconmanager.hpp: * code/core/iconmanager.cpp: Added a stock item for the user color indicator. * code/core/userlist.cpp: Show the user color indicator for the user color instead of a boring monochromatic square. 2009-10-24 Benjamin Herr * icons/hicolor/48x48/status/user-color-indicator.png: * icons/hicolor/scalable/status/user-color-indicator.svg: Added fancier icon to display the user colors in the user list. Not actually implemented. 2009-10-06 Armin Burgmeier * code/core/docwindow.cpp: Restore the "highlight matching brackets" option correctly from the preferences. Bug #470. 2009-09-14 Armin Burgmeier * code/commands/file-tasks/task-open-multiple.hpp: Added an include to fix the build. Bug #469. 2009-09-09 Armin Burgmeier * code/window.cpp (on_message_received): Don't leak the uris vector when an exception is thrown. 2009-09-09 Benjamin Herr * code/window.cpp: * code/window.hpp: * code/main.cpp: Also send hostnames given with --connect to an already running instance of gobby (via libunique). 2009-09-08 Benjamin Herr * code/core/browser.hpp: * code/core/browser.cpp: * code/window.hpp: * code/window.cpp: * code/main.cpp: Added --connect=HOSTNAME command line flag to connect to the given hostnames on startup. 2009-08-30 Armin Burgmeier * configure.ac: Require glib-2.18, as replacing existing files while saving does not work with lower versions. * README: Added the new dependency to the README. 2009-08-30 Armin Burgmeier * README: Updated the text, parts of it were still from when we used darcs (Nicolás Alvarez). 2009-08-29 Armin Burgmeier * update-potfiles: Fixed path of the desktop file, and use --binary-file=without-matches instead of a checking for the string 'matches', so that it also works in non-english environments (Nicolás Alvarez). 2009-06-05 Philipp Kern * gobby-0.5.desktop.in: fix icon to not specify the extension, which is discouraged by the icon theme specification 2009-06-03 Armin Burgmeier * code/core/header.cpp: Replace localized section string by a numerical ID for the action names of the various language sections, to prevent a crash when starting Gobby on a Japanese system. Based on a patch by Takahiro Sunaga. 2009-05-26 Armin Burgmeier * code/core/browser.hpp: * code/core/browser.cpp: Changed the HistoryEntry to a HistoryComboBoxEntry, so that we have a dropdown list for recently used hosts. Bug #451. 2009-05-17 Philipp Kern * help/C/gobby.xml: ported content from the old readme to the help file 2009-05-16 Armin Burgmeier * code/commands/file-tasks/task-open.hpp: Include to make sure Gio::File is known. * code/commands/file-tasks/task-open.cpp: Removed an unused variable. 2009-05-16 Armin Burgmeier * docs/: Removed. This was outdated anyway. It should be updated and merged into the new DocBook help in help/. See also #448. 2009-05-16 Armin Burgmeier * Makefile.am: Don't define EXTRA_DIST twice. 2009-05-16 Armin Burgmeier * help/C/gobby.xml: Fixed Gobby command line invocation instruction: The executable is called gobby-0.5, not gobby. 2009-05-16 Armin Burgmeier * code/core/tablabel.cpp: Use gdk_color_to_string instead of Gdk::Color::to_string so that Gobby continues to compile with gtkmm 2.12. Bug #447 (Philipp Kern). 2009-05-12 Philipp Kern * code/dialogs/initial-dialog.cpp: match menuchoice style with manual 2009-05-12 Philipp Kern * configure.ac: get rid of WITH_GNOME, as it is currently unused 2009-05-12 Philipp Kern * autogen.sh: let autogen fail if some command is not available or returns with a failure 2009-05-11 Philipp Kern * code/core/header.cpp: use F1 for HelpContents, instead of the stock Ctrl-H 2009-05-11 Philipp Kern * code/core/header.hpp: * code/core/header.cpp: add HelpContents action * code/commands/help-commands.hpp: * code/commands/help-commands.cpp: invoke help through gtk_show_uri 2009-05-10 Philipp Kern * help/C/gobby.xml: initial manual draft, still pretty empty * help/Makefile.am: * Makefile.am: * autogen.sh: * configure.ac: use gnome-doc-utils as specified in their manual * help/C/figures/main_window.png: * help/C/figures/preferences_appearance.png: * help/C/figures/preferences_editor.png: * help/C/figures/preferences_security.png: * help/C/figures/preferences_user.png: * help/C/figures/preferences_view.png: add an initial set of screenshots (main window and preference panes) 2009-05-10 Philipp Kern * contrib/gobby-0.5.desktop.in: moved to the toplevel * Makefile.am: install the .desktop file unconditionally (i.e. not only WITH_GNOME) * contrib/*: removed 2009-05-03 Armin Burgmeier , Benjamin Herr * configure.ac: Added a --with-unique configure option, require unique-1.0 if set. * code/core/nodewatch.hpp: * code/core/nodewatch.cpp: Accept a const InfcBrowserIter, and changed the signature of get_browser_iter() to return the BrowserIter instead of setting a reference parameter. * code/operations/operation-new.cpp: * code/operations/operation-new.hpp: * code/operations/operation-open.cpp: * code/operations/operation-open.hpp: * code/operations/operation-delete.cpp: * code/operations/operation-delete.hpp: Accept a const InfcBrowserIter in the constructor. * code/operations/operation-open-multiple.hpp: * code/operations/operation-open-multiple.cpp: Added an operation which opens multiple files into the same directory. This is nothing which can't be achieved by creating multiple OperationOpen operations, but it creates the documents one-after-the-other, and therefore does not clutter the statusbar, plus it queries the display name of a file automatically to use as document name, if no document name is given explicitely. * code/operations/operations.hpp: * code/operations/operations.cpp: Accept a const InfcBrowserIter for most operations, added the create_documents() method which creates an OperationOpenMultiple. * code/commands/file-commands.hpp: Make set_task() public. We should later make a separate class for managing the file tasks. * code/dialogs/document-location-dialog.hpp: * code/dialogs/document-location-dialog.cpp: Added a multiple document mode to load multiple files. This basically hides the document name entry. * code/commands/file-tasks/task-new.cpp: * code/commands/file-tasks/task-open.cpp: Explicitely set single document mode on the document location dialog. * code/commands/file-tasks/task-open-multiple.hpp: * code/commands/file-tasks/task-open-multiple.cpp: Added a new task to open multiple files. This shows a document location dialog in multiple document mode and launches an OperationOpenMultiple when the user is done with the dialog. * code/commands/file-tasks/task-save-all.cpp: Added a TODO comment about splitting parts of this into an OperationSaveAll. * code/window.hpp: * code/window.cpp: Take command line arguments in the constructor, and open documents passed on the command line using a TaskOpenMultiple. If unique support is enabled, then react on incoming messages and activate the application window, or open files received in a message. * code/main.cpp: Added a --new-instance command line option. If not set and compiled with unique support, and if another instance is already running, then send a message to the running instance and exit. * Makefile.am: * code/Makefile.am: * code/util/Makefile.am: * code/core/Makefile.am: * code/dialogs/Makefile.am: * code/operations/Makefile.am: * code/commands/Makefile.am: * code/commands/file-tasks/Makefile.am: Added compiler and linker flags for unique, added the new files to the build. * code/commands/autosave-commands.cpp: Fixed two compiler warnings. 2009-05-01 Armin Burgmeier * code/core/nodewatch.hpp: * code/core/nodewatch.cpp: Removed the model from NodeWatch, instead connect to browser's connection's notify::status to detect disconnection. * code/commands/browser-context-commands.cpp: Adapt. 2008-05-01 Armin Burgmeier * gobby-0.5.xpm: Added a 32x32 XPM of the gobby icon. * Makefile.am: Distribute it. 2009-04-28 Benjamin Herr * code/commands/file-tasks/task-open.cpp: Do not delay reporting errors in Gobby::TaskOpen::run() anymore, since signal_finished will already be connected to. 2009-04-27 Armin Burgmeier * code/dialogs/document-location-dialog.cpp: Removed operator== for two GtkTextIters, as it is not used anymore. We are forced to compare TextPaths anyway. 2009-04-27 Armin Burgmeier * code/commands/file-tasks/task-save.cpp: Initialize m_running to false in constructor, and set m_running to true in run(). 2009-04-27 Benjamin Herr * code/commands/file-commands.cpp: * code/commands/file-commands.hpp: * code/commands/file-tasks/task-new.cpp: * code/commands/file-tasks/task-new.hpp: * code/commands/file-tasks/task-open-file.cpp: * code/commands/file-tasks/task-open-file.hpp: * code/commands/file-tasks/task-open-location.cpp: * code/commands/file-tasks/task-open-location.hpp: * code/commands/file-tasks/task-open.cpp: * code/commands/file-tasks/task-open.hpp: * code/commands/file-tasks/task-save-all.cpp: * code/commands/file-tasks/task-save-all.hpp: * code/commands/file-tasks/task-save.cpp: * code/commands/file-tasks/task-save.hpp: Split most of all Task constructors into a separate run() member function that is called by FileCommands::set_task after resetting its current task. This ensures that a new task only gets to mess with stuff after the old one's destructor has been run. 2009-04-26 Armin Burgmeier * README: * configure.ac: Post-release bump to 0.4.93, require libfinity-0.4. 2009-04-26 Armin Burgmeier * === Released 0.4.92 === * README: * NEWS: Update for release. 2009-04-25 Armin Burgmeier * code/core/tablabel.hpp: * code/core/tablabel.cpp: Added a fallback chain for the dot character if the current font doesn't support the pencil glyph. 2009-04-24 Armin Burgmeier * code/main.cpp: Changed locale lookup directory on Windows from lib/locale to share/locale. 2009-04-24 Armin Burgmeier * configure.ac: Changed PACKAGE name back to gobby. * icons/HighContrastLargePrint/48x48/actions/Makefile.am: * icons/HighContrastLargePrint/scalable/actions/Makefile.am: * icons/HighContrastLargePrintInverse/48x48/actions/Makefile.am: * icons/HighContrastLargePrintInverse/scalable/actions/Makefile.am: * icons/hicolor/48x48/actions/Makefile.am: * icons/hicolor/scalable/actions/Makefile.am: * code/core/Makefile.am: Use $(datadir)/gobby-0.5 instead of $(pkgdatadir), so we still install the private icons into a separate directory. 2009-04-22 Benjamin Herr * code/core/tablabel.hpp: * code/core/tablabel.cpp: Display pencil icons in user hue for each user that has edited a document since it was last viewed (Bug #66 by Mattis Manzel). 2009-04-20 Benjamin Herr * code/commands/file-tasks/task-new.cpp: Removed spurious semicolon. 2009-04-20 Armin Burgmeier * code/core/browser.cpp: * code/core/documentinfostorage.cpp: * code/dialogs/open-location-dialog.cpp: * code/main.cpp: * code/util/file.cpp: * code/util/file.hpp: * configure.ac: Store config in Glib::get_user_config_dir() everywhere instead of only on Win32 (Bug #437 by Cristian Klein). 2009-04-20 Armin Burgmeier * configure.ac: Adapt to libinfinity becoming parallel-installable. 2009-02-28 Armin Burgmeier * code/operations/operation-save.cpp: When saving files, don't write almost every line twice if the first line is empty. 2009-02-26 Armin Burgmeier * code/util/resolv.cpp: Added include to fix the build on Free BSD. Bug #431. Patch by Romain Tartière. 2009-02-14 Armin Burgmeier * code/util/config.hpp: * code/util/config.cpp: Added ParentEntry::has_value(). * code/core/preferences.cpp: Added /etc/ssl/certs/ca-certificates.crt as default value for the trust-file, if the file exists. 2009-02-03 Armin Burgmeier * code/operations/operation-save.cpp: Make sure terminating newline characters are saved correctly. 2009-02-02 Armin Burgmeier * pixmaps/: Removed the pixmaps directory. The pixmaps are now installed as icons, to respect theme settings. * contrib/artwork/: Removed as well, since all artwork is now contained in icons/ anyway. * contrib/Makefile.am: * Makefile.am: Adapt for the removal of the above directories. * icons/hicolor/48x48/actions/chat.png: * icons/hicolor/48x48/actions/document-list.png: * icons/hicolor/48x48/actions/user-list.png: * icons/hicolor/48x48/actions/Makefile.am: * icons/hicolor/48x48/Makefile.am: * icons/hicolor/scalable/actions/chat.svg: * icons/hicolor/scalable/actions/document-list.svg: * icons/hicolor/scalable/actions/user-list.svg: * icons/hicolor/scalable/actions/Makefile.am: * icons/hicolor/scalable/Makefile.am: Added previous pixmaps as icons, so they are themeable. * icons/HighContrastLargePrintInverse/48x48/actions/chat.png: * icons/HighContrastLargePrintInverse/48x48/actions/document-list.png: * icons/HighContrastLargePrintInverse/48x48/actions/user-list.png: * icons/HighContrastLargePrintInverse/48x48/actions/Makefile.am: * icons/HighContrastLargePrintInverse/48x48/apps/gobby.png: * icons/HighContrastLargePrintInverse/48x48/Makefile.am: * icons/HighContrastLargePrintInverse/scalable/actions/chat.svg: * icons/HighContrastLargePrintInverse/scalable/actions/document-list.svg: * icons/HighContrastLargePrintInverse/scalable/actions/user-list.svg: * icons/HighContrastLargePrintInverse/scalable/actions/Makefile.am: * icons/HighContrastLargePrintInverse/scalable/apps/gobby.svg: * icons/HighContrastLargePrintInverse/scalable/Makefile.am: Added corresponding HCLPI icons (thanks Tom). * icons/HighContrastLargePrint/48x48/actions/chat.png: * icons/HighContrastLargePrint/48x48/actions/document-list.png: * icons/HighContrastLargePrint/48x48/actions/user-list.png: * icons/HighContrastLargePrint/48x48/actions/Makefile.am: * icons/HighContrastLargePrint/48x48/apps/gobby.png: * icons/HighContrastLargePrint/48x48/apps/Makefile.am: * icons/HighContrastLargePrint/48x48/Makefile.am: * icons/HighContrastLargePrint/scalable/actions/chat.svg: * icons/HighContrastLargePrint/scalable/actions/document-list.svg: * icons/HighContrastLargePrint/scalable/actions/user-list.svg: * icons/HighContrastLargePrint/scalable/actions/Makefile.am: * icons/HighContrastLargePrint/scalable/apps/gobby.svg: * icons/HighContrastLargePrint/scalable/apps/Makefile.am: * icons/HighContrastLargePrint/scalable/Makefile.am: * icons/HighContrastLargePrint/Makefile.am: Added corresponding HCLP icons (again, thanks Tom). * icons/Makefile.am: Descend into HigtContrastLargePrint subdirectory. * code/core/iconmanager.hpp: * code/core/iconmanager.cpp: Load icons from icon name instead of directly from their path, added priate icons directory to icon theme search path. * code/util/Makefile.am: * code/core/Makefile.am: * code/dialogs/Makefile.am: * code/operations/Makefile.am: * code/commands/file-tasks/Makefile.am: * code/commands/Makefile.am: * code/Makefile.am: Removed APPICON_DIR and PIXMAPS_DIR definitions, as these are no longer required. * configure.ac: Generate newly added Makefiles, changed package name to gobby-0.5, so that the private icons end up in their own directory, parallel-installable with the 0.4 line. 2009-02-01 Armin Burgmeier * code/dialogs/find-dialog.hpp: * code/dialogs/goto-dialog.hpp: * code/dialogs/preferences-dialog.hpp: * code/dialogs/entry-dialog.hpp: * code/dialogs/document-location-dialog.hpp: * code/dialogs/open-location-dialog.hpp: * code/dialogs/initial-dialog.hpp: * code/dialogs/find-dialog.cpp: * code/dialogs/goto-dialog.cpp: * code/dialogs/preferences-dialog.cpp: * code/dialogs/entry-dialog.cpp: * code/dialogs/document-location-dialog.cpp: * code/dialogs/open-location-dialog.cpp: * code/dialogs/initial-dialog.cpp: * code/dialogs/Makefile.am: Added hyphens into the file names, to stay consistent with other code files, such as commands/ or operations/. * code/commands/file-tasks/task-open-location.hpp: * code/commands/browser-context-commands.hpp: * code/commands/edit-commands.hpp: * code/commands/file-commands.hpp: * code/window.hpp: Adapt #include directives. 2009-02-01 Armin Burgmeier * code/commands/file-tasks/task-new.hpp: * code/commands/file-tasks/task-open.hpp: * code/commands/file-tasks/task-open-file.hpp: * code/commands/file-tasks/task-open-location.hpp: * code/commands/file-tasks/task-save.hpp: * code/commands/file-tasks/task-save-all.hpp: * code/commands/file-tasks/task-new.cpp: * code/commands/file-tasks/task-open.cpp: * code/commands/file-tasks/task-open-file.cpp: * code/commands/file-tasks/task-open-location.cpp: * code/commands/file-tasks/task-save.cpp: * code/commands/file-tasks/task-save-all.cpp: * code/commands/file-tasks/Makefile.am: Splitted the tasks which were defined in code/commands/file-commands.cpp into multiple files. * code/commands/file-commands.hpp: * code/commands/file-commands.cpp: Adapt accordingly. * code/commands/Makefile.am: Descend into file-tasks subdirectory. * Makefile.am: Link against the new tasks. * configure.ac: Create the new Makefiles. 2009-01-30 Armin Burgmeier * code/util/resolv.cpp: Removed the _WIN32_WINNT #define which is no longer required now that we use for getaddrinfo support. 2009-01-30 Armin Burgmeier * icons/HighContrastLargePrintInverse/48x48/Makefile.am: * icons/HighContrastLargePrintInverse/48x48/apps/Makefile.am: * icons/HighContrastLargePrintInverse/48x48/apps/gobby.png: * icons/HighContrastLargePrintInverse/scalable/Makefile.am: * icons/HighContrastLargePrintInverse/scalable/apps/Makefile.am: * icons/HighContrastLargePrintInverse/scalable/apps/gobby.svg: * icons/hicolor/48x48/Makefile.am: * icons/hicolor/48x48/apps/Makefile.am: * icons/hicolor/48x48/apps/gobby.png: * icons/hicolor/scalable/Makefile.am: * icons/hicolor/scalable/apps/Makefile.am: * icons/hicolor/scalable/apps/gobby.svg: * configure.ac: Added context ("apps") to icons directory structure. 2009-01-30 Armin Burgmeier * code/util/resolv.cpp: Include the header file on Windows to support Windows 2000. Bug #425. 2009-01-16 Armin Burgmeier * code/commands/file-commands.cpp: Don't use Gtk::FileChooser::get_file(), so that we don't require gtkmm 2.14. 2009-01-16 Armin Burgmeier * code/commands/file-commands.cpp: Marked a string for localization. 2009-01-15 Armin Burgmeier * all source files: Added 2009 to copyright years. 2009-01-04 Armin Burgmeier * configure.ac: Post-release bump to 0.4.92. 2009-01-04 Armin Burgmeier * === Released 0.4.91 === * NEWS: Update for release. 2009-01-04 Armin Burgmeier * code/core/window.cpp: Re-enable shortcuts for Undo and Redo. These have been accidentally disabled when pasting text in the "Direct Connection" entry has been fixed. 2009-01-04 Armin Burgmeier * code/core/docwindow.hpp: * code/core/docwindow.cpp: Show a tooltip when hovering over text, saying what user wrote the hovered text. 2009-01-04 Armin Burgmeier * code/core/window.hpp: * code/core/window.cpp: Fixed pasting text into the direct connection entry when having open a document. Before, the text was pasted into the document even though the entry was focused. We use the same workaround as gedit. 2009-01-04 Armin Burgmeier * code/core/folder.cpp: Re-enable Ctrl+Alt+PgDown/Ctrl+Alt+PgUp to cycle between tabs. * code/core/docwindow.cpp: Added a TODO comment about using a single UserList widget for all documents. 2009-01-04 Armin Burgmeier * code/core/statusbar.hpp: * code/core/statusbar.cpp: Added an invalid_handle() function, so other code can keep around invalid message handles, and fixed invalid memory access when a document was removed. * code/commands/browser-context-commands.cpp: Added a TODO comment about using file-tasks for common operations. * code/commands/file-commands.cpp: Query file info asynchronously, to avoid UI hangs when opening unresponsive remote files. 2009-01-03 Armin Burgmeier * code/util/historyentry.hpp: * code/util/historyentry.cpp: Added HistoryComboBoxEntry, which allows to select the history entries via a combo box. * code/dialogs/openlocationdialog.hpp: * code/dialogs/openlocationdialog.cpp: Use a HistoryComboBoxEntry instead of a simple GtkEntry. * code/dialogs/entrydialog.cpp: Don't use Gtk::Entry::get_text_length() since this is available only since gtkmm 2.14, but we require only 2.12. 2009-01-03 Armin Burgmeier * code/dialogs/openlocationdialog.hpp: * code/dialogs/openlocationdialog.cpp: * code/dialogs/Makefile.am: Added a dialog which queries the user for an URI to open. * code/core/header.hpp: * code/core/header.cpp: Added an "Open Location" menu item. * code/commands/file-commands.hpp: * code/commands/file-commands.cpp: Handle the "Open Location" menu item, fixed deinitialization order. * code/window.cpp: Pass statusbar to file commands. * code/operations/operation-open.cpp: Added error handling for the initial read_async operation. 2009-01-02 Armin Burgmeier * code/util/historyentry.hpp: * code/util/historyentry.cpp: * code/util/Makefile.am: Added a HistoryEntry class which allows scrolling through past entries in a GtkEntry, optionally saving and restoring the history from/to disk. * code/core/browser.hpp: * code/core/browser.cpp: Use a history entry for the hostname entry. 2008-12-31 Armin Burgmeier * code/core/preferences.hpp: * code/core/preferences.cpp: Added autosave_enabled and autosave_interval properties. * code/dialogs/preferencesdialog.hpp: * code/dialogs/preferencesdialog.cpp: Added the two preferences to the user interface. * code/commands/autosave-commands.hpp: * code/commands/autosave-commands.cpp: * code/commands/Makefile.am: Implemented autosave. * code/window.hpp: * code/window.cpp: Instantiate AutosaveCommands. * code/commands/file-commands.cpp: Added a TODO comment about defaulting to CRLF on Windows. 2008-12-31 Armin Burgmeier * code/operations/operations.hpp: * code/operations/operations.cpp: Added a begin_save_operation signal, and added a boolean parameter to base operation's finished signal which indicates whether the operation was performed successfully or not. Removed the remove() function, and added finish() and fail() instead. * code/operations/operation-open.hpp: Derive from sigc::trackable. * code/operations/operation-save.hpp: * code/operations/operation-save.cpp: Added get_document(), get_start_time() and derive from sigc::trackable, adapt to finish() and fail(). * code/operations/operation-new.cpp: * code/operations/operation-open.cpp: * code/operations/operation-delete.cpp: Adapt to finish() and fail(). 2008-12-30 Armin Burgmeier * code/operations/operations.hpp: * code/operations/operations.cpp: Added signal_finished to the operation base class, and the various functions creating operations now return them. * code/operations/operation-save.cpp: Fixed a typo in a comment. 2008-12-29 Armin Burgmeier * code/window.hpp: * code/window.cpp: Changed (de)initialization order so that browser is freed after folder. This is required to avoid a warning when an InfSession holds a reference to a connection (such as during synchronization) and Gobby is closed. 2008-12-28 Armin Burgmeier * configure.ac: Require libinfinity >= 0.2. 2008-12-28 Armin Burgmeier * code/core/browser.cpp: * code/core/noteplugin.cpp: * code/operations/operation-open.cpp: Adapt to libinfinity's new communication API. 2008-12-23 Philipp Kern * configure.ac: separate infinote pkg-config from those determining if all libraries needed for Gobby's user interface are present, allowing easier and separate overrides of them * Makefile.am: * code/Makefile.am: * code/commands/Makefile.am: * code/core/Makefile.am: * code/dialogs/Makefile.am: * code/operations/Makefile.am: * code/util/Makefile.am: use AM_CPPFLAGS for everything instead of specialized per-target CPPFLAGS entries 2008-12-23 Philipp Kern * code/Makefile.am: * code/commands/Makefile.am: * code/core/Makefile.am: * code/dialogs/Makefile.am: * code/operations/Makefile.am: * code/util/Makefile.am: Include top-level CFLAGS. 2008-12-19 Armin Burgmeier * po/fr.po: Changed application name to "Editeur collaboratif Gobby" to meet French Gnome standard. 2008-12-19 Armin Burgmeier * po/fr.po: Added translation for the desktop file (Bug #415, Pascal Kreyer). 2008-12-19 Armin Burgmeier * po/de.po: Removed dash from application name, as other GNOME programs do. 2008-12-17 Armin Burgmeier * icons/hicolor/48x48/gobby-0.5.png: * icons/hicolor/48x48/Makefile.am: * icons/hicolor/Makefile.am: Added fixed-size version of gobby icon. * icons/HighContrastLargePrintInverse/48x48/gobby-0.5.png: * icons/HighContrastLargePrintInverse/48x48/Makefile.am: * icons/hicolor/Makefile.am: Added fixed-size version of HCLPI gobby icon. * configure.ac: Generate the new Makefiles. 2008-12-17 Armin Burgmeier * contrib/artwork/gobby.svg: Centered the gobby icon into the document. * code/core/iconmanager.hpp: * code/core/iconmanager.cpp: Removed the gobby icon pixbuf. * code/dialogs/initialdialog.cpp: * code/commands/help-commands.cpp: * code/main.cpp: Lookup the gobby icon by its icon name. * pixmaps/gobby-0.5.png: Deleted. * icons/hicolor/scalable/gobby-0.5.svg: Added scalable gobby icon. * icons/hicolor/scalable/Makefile.am: Install it into a proper location. * icons/hicolor/Makefile.am: Run update-icon-cache on hicolor theme after (un)intall. * icons/HighContrastLargePrintInverse/scalable/gobby-0.5.svg: Added scalable gobby icon for HCLPI theme (Thomas Glatt, Bug #413). * icons/HighContrastLargePrintInverse/scalable/Makefile.am: Install it. * icons/HighContrastLargePrintInverse/Makefile.am: Run update-icon-cache on HighContrastLargePrintInverse after (un)install. * Makefile.am: Decend into the icons/ subdir. * pixmaps/Makefile.am: Don't install gobby-0.5.png to share/pixmaps anymore. * configure.ac: Generate the new Makefiles. 2008-11-23 Armin Burgmeier * configure.ac: Post-release bump to 0.4.91. 2008-11-23 Armin Burgmeier * contrib/Makefile.am: Always add gobby-0.5.desktop.in to the distribution. 2008-11-23 Armin Burgmeier * Makefile.am: Add a dummy C++ file to nondistributed sources, to cause C++ linking. Bug #399, Markus Wanjura. 2008-11-23 Armin Burgmeier * code/core/browser.cpp: Added include, required on Mac OS X. Bug #399, Markus Wanjura. 2008-11-23 Armin Burgmeier * code/util/i18n.hpp: Added definition for N_. * code/operations/operation-open.cpp: Made 8bit encoding used for auto detection translatable, so that locales with other default 8bit encodings than iso-8859-1 get better results for auto detection. 2008-11-23 Armin Burgmeier * contrib/gobby-0.5.desktop: Removed. * contrib/gobby-0.5.desktop.in: Added, marking name and comment for translation. * contrib/Makefile.am: Generate contrib/gobby-0.5.desktop from contrib/gobby-0.5.desktop.in via intltool. This allows the desktop file to be translated via po files. Bug #396, based on a patch from Takao Fujiwara. * autogen.sh: Run intltoolize. * configure.ac: Require intltool. * update-potfiles: Added contrib/gobby-0.5.desktop.in to list of files scanned for translatable strings. 2008-11-09 Armin Burgmeier * code/commands/edit-commands.cpp: Scroll to cursor after cut or paste. 2008-11-05 Armin Burgmeier * code/commands/edit-commands.cpp: Added a (hopefully) temporary hack to set the cursor position to the place where a Undo/Redo has occurred. This stopped to work with the latest changes in libinfinity (which in turn fix other caret stuff, such as no longer moving the caret around on block indent/unindent, or allowing to paste text over a selected area). This can be properly implemented as soon as libinfinity supports caret-aware requests. 2008-10-18 Armin Burgmeier * code/core/browser.cpp: * code/core/header.cpp: Use standard capitalization for control labels. Bug #223 (Matthew Paul Thomas). 2008-10-18 Armin Burgmeier * code/main.cpp: Set localedir relative to Gobby executable on Windows, so i18n is properly relocatable. 2008-10-18 Armin Burgmeier * === Released 0.4.90 === 2008-10-17 Armin Burgmeier * contrib/gobby-0.5.desktop: Fixed icon file name. 2008-10-16 Armin Burgmeier * code/Makefile.am: * code/commands/Makefile.am: * code/core/Makefile.am: * code/dialogs/Makefile.am: * code/operations/Makefile.am: * code/util/Makefile.am: Fix `make distcheck' by using -I$(top_srcdir) instead of a relative path such as -I.. Also, changed pixmapdir to $(datadir)/pixmaps/gobby-0.5. * pixmaps/Makefile.am: * pixmaps/gobby-0.5.png: Moved from pixmaps/gobby.png. * contrib/Makefile.am: * contrib/gobby-0.5.desktop: Moved from gobby.desktop. * configure.ac: Changed GETTEXT_PACKAGE from gobby to gobby05. * code/core/iconmanager.cpp: Load gobby-0.5.png for the Gobby logo instead of gobby.png. 2008-10-16 Armin Burgmeier * NEWS: Update for release. 2008-10-16 Armin Burgmeier * all source files: Changed description in GPL header since Gobby is no longer libobby driven. Also changed Copyright holder to me and Copyright date to 2008 since 99% of the code was written by me in 2008. 2008-10-16 Armin Burgmeier * contrib/gobby.desktop: Launch the correct executable, added a "(0.5)" to the description to differentiate it from a previous parallel-installed version. 2008-10-16 Armin Burgmeier * code/commands/browser-commands.cpp (on_synchronization_failed): Remove the entry from the session map directly instead of waiting for session closure, otherwise the error message is overwritten with a useless (and incorrent) 'The connection to the publisher has been lost' one. 2008-10-15 Armin Burgmeier * code/core/browser.hpp: * code/core/browser.cpp: Adapt to libinfinity API change, INFINOTE_HAVE_AVAHI has been renamed to LIBINFINITY_HAVE_AVAHI. 2008-10-15 Armin Burgmeier * code/core/Makefile.am: Added appicondir and pixmapdir back, so that installed pixmaps can be found. * code/util/resolv.cpp: Set all fields in getaddrinfo hint, otherwise the Win32 implementation of getaddrinfo produces an error. * code/core/browser.cpp: Commented-out the device name to device index translation for %dev syntax on Windows. It won't be available for now. * code/core/iconmanager.cpp: Don't load save-all.svg since GTK+ on Win32 has no SVG loader by defualt. Also, locate pixbufs using the non-deprecated g_win32_get_package_installation_directory_of_module function. * code/core/statusbar.cpp: Added code that undefs ERROR, because this is used in an enum, and seems to be defined somewhere in a Windows header. * code/dialogs/preferencesdialog.cpp: Don't set current folder of host directory file chooser (which is not shown currently anyway), because this could produce an error message if the directory does not exist. * code/operations/operation-open.cpp: * code/operations/operation-save.cpp: Use the C g_iconv API instead of Glib::IConv::iconv, because iconv is a define to libiconv on Windows, but Glib::IConv::libiconv does not exist. * Makefile.am: Fixed linking order, link to stdc++ and ws2_32 on Windows. * win32/gobby.rc: Fixed path to gobby.res file. 2008-10-15 Armin Burgmeier * po/LINGUAS: Added LINGUAS file that lists all languages. This seems to be required for `make dist'. * configure.ac: Use the po/LINGUAS file to set the ALL_LINGUAS variable. 2008-10-14 Armin Burgmeier * code/main.cpp: Call inf_init() instead of initializing gnutls and the GObject type system directly. 2008-10-14 Armin Burgmeier * code/commands/browser-commands.cpp: Fixed a typo in a string. 2008-10-14 Armin Burgmeier * code/commands/folder-commands.cpp: Only wake inactive users when the corresponding document is the currently active one. This fixes a crash when text has was selected in an inactive document and the selection is erased by selecting something elsewhere. 2008-10-13 Armin Burgmeier * code/core/titlebar.hpp: * code/core/titlebar.cpp: * code/core/Makefile.am: Added a new class that handles what is displayed in the titel bar of the Gobby window. * code/window.hpp: * code/window.cpp: Instantiate a TitleBar. * code/core/documentinfostorage.hpp: Added a superflous TODO comment. 2008-10-13 Armin Burgmeier * code/commands/browser-commands.hpp: * code/commands/browser-commands.cpp: * code/operations/operation-open.cpp: * code/operations/operation-save.cpp: Set the modified flag upon opening or saving a document correctly. * code/core/tablabel.hpp: * code/core/tablabel.cpp: Show the modify flag in the tab. 2008-10-12 Armin Burgmeier * code/operations/operation-open.cpp: Fixed double unref of IO object. * code/core/browser.cpp: Added a TODO comment about initial root node expansion for direct connections. 2008-10-11 Armin Burgmeier * code/core/tablabel.hpp: * code/core/tablabel.cpp: * code/core/Makefile.am: Added TabLabel to this extra source file, make color of tab label red on activity, and grey when the document is being synchronized or the connection has been lost. * code/core/tablabel.cpp: Removed the TabLabel class in the anonymous namespace, use the one introduced above instead. 2008-10-10 Armin Burgmeier * code/core/filechooser.cpp: Allow selecting non-local files in Gobby's filechooser. * code/commands/file-commands.cpp: * code/commands/browser-context-commands.cpp: Query the display name when opening a file, to use as document title. FileChooser::get_filename() does not work for non-local files. 2008-10-09 Armin Burgmeier * code/core/iconmanager.cpp: Removed the Save All icon from display, because it does not fit to the "Save" icon for Toolbar and Menu sizes. * code/core/header.cpp: Remove Save All icon from the toolbar. Items without icons look ugly and the toolbar, and there is a shortcut for quick access. 2008-10-09 Armin Burgmeier * code/util/file.hpp: * code/util/file.cpp: Added the config_filename() function. * code/core/browser.cpp: * code/core/documentinfostorage.cpp: * code/main.cpp: Make use of that function to access config files. * code/window.cpp: Give initial focus to browser. 2008-10-09 Armin Burgmeier * configure.ac: Renamed the `OSX' define to `PLATFORM_OSX', and added `PLATFORM_OSX_NATIVE' when running with the quartz GDK target. * code/core/header.cpp: Adapt, only use ige-mac-integration when running natively on OS X. 2008-10-09 Armin Burgmeier * code/: Moved from src/. * code/commands/browser-commands.hpp: * code/commands/browser-context-commands.hpp: * code/commands/edit-commands.hpp: * code/commands/file-commands.hpp: * code/commands/folder-commands.hpp: * code/commands/help-commands.hpp: * code/commands/view-commands.hpp: * code/core/browser.hpp: * code/core/closableframe.hpp: * code/core/documentinfostorage.hpp: * code/core/docwindow.hpp: * code/core/filechooser.hpp: * code/core/folder.hpp: * code/core/header.hpp: * code/core/huebutton.hpp: * code/core/iconmanager.hpp: * code/core/nodewatch.hpp: * code/core/noteplugin.hpp: * code/core/preferences.hpp: * code/core/statusbar.hpp: * code/core/userlist.hpp: * code/dialogs/documentlocationdialog.hpp: * code/dialogs/entrydialog.hpp: * code/dialogs/finddialog.hpp: * code/dialogs/gotodialog.hpp: * code/dialogs/initialdialog.hpp: * code/dialogs/preferencesdialog.hpp: * code/operations/operation-delete.hpp: * code/operations/operation-new.hpp: * code/operations/operation-open.hpp: * code/operations/operation-save.hpp: * code/operations/operations.hpp: * code/util/closebutton.hpp: * code/util/color.hpp: * code/util/config.hpp: * code/util/defaultaccumulator.hpp: * code/util/file.hpp: * code/util/i18n.hpp: * code/util/resolv.hpp: * code/util/serialize.hpp: * code/features.hpp: * code/window.hpp: Moved from inc/. * code/commands/Makefile.am: * code/operations/Makefile.am: * code/dialogs/Makefile.am: * code/core/Makefile.am: * code/util/Makefile.am: * code/Makefile.am: * Makefile.am: * configure.ac: Adapt to the new build structure. 2008-10-09 Armin Burgmeier * src/Makefile.am: Rename the gobby binary to gobby-0.5, to support parallel installation with gobby 0.4. 2008-10-09 Armin Burgmeier * inc/core/docwindow.hpp: * src/core/docwindow.cpp: Added the scroll_to_cursor_position() function. * src/commands/edit-commands.cpp: Scroll to cursor position on Undo or Redo. 2008-10-08 Armin Burgmeier * src/core/folder.cpp: Emit document_changed with NULL document after the last document has been removed, not before, so get_current_document() yields the correct result. * inc/commands/edit-commands.hpp: * src/commands/edit-commands.cpp: Don't rely on m_current_document being valid until on_document_changed() was called. * inc/commands/file-commands.hpp: * src/commands/file-commands.cpp: Make "New" and "Open" actions insensitive when there is no entry in the Document Browser. 2008-10-08 Armin Burgmeier * src/core/browser.cpp: Adapt to infinote API changes. 2008-10-01 Armin Burgmeier * src/util/config.cpp: Renamed the root config from gobby_config to gobby-config. 2008-09-30 Armin Burgmeier * src/commands/folder-commands.cpp: Added another assertion to be able to better track another problem with users becoming active without having the document selected in the notebook. 2008-09-22 Armin Burgmeier * src/commands/folder-commands.cpp: Flush pending user requests before setting user inactive, to avoid the user becoming active again due to a delayed caret movement. 2008-09-22 Armin Burgmeier * src/core/preferences.cpp: Fixed writing the insert-tabs-instead-of-spaces option to the config file. 2008-09-22 Armin Burgmeier * src/core/huebutton.cpp: Fix button ordering. 2008-09-21 Armin Burgmeier * src/main.cpp: Display version information on -v. 2008-09-21 Armin Burgmeier * src/core/docwindow.cpp: Make sure author tags have lower priority than GtkSourceView tags. 2008-09-21 Armin Burgmeier * src/window.cpp: Set main window role. 2008-09-21 Armin Burgmeier * inc/dialogs/preferenceswindow.hpp: Don't notify option if the newly selected option is the same as the already selected one. 2008-09-21 Armin Burgmeier * src/commands/file-fommands.cpp: Use document title as initial filename for Save dialog if no URI is available in info storage. 2008-09-21 Armin Burgmeier * src/commands/view-commands.cpp: Set "View Userlist" insensitive if no document is open. 2008-09-21 Armin Burgmeier * inc/core/browser.hpp: * src/core/browser.cpp: Support hostname:port%device syntax for direct connections, to be able to establish IPv6 link-local connections directly. 2008-09-18 Armin Burgmeier * update-potfiles: Added a small script to generate the po/POTFILES.in. It is simple, but enough for our needs. 2008-09-18 Armin Burgmeier * src/commands/browser-commands.cpp: * src/commands/browser-context-commands.cpp: * src/core/browser.cpp: * src/core/header.cpp: * src/operations/operation-delete.cpp: * src/operations/operation-new.cpp: * src/operations/operation-open.cpp: * src/operations/operation-save.cpp: Changed all "…" character in strings to "...". Gedit does the same, and xgettext does not accept … without complaining. Translators can still choose to translate "..." to "…". 2008-09-18 Armin Burgmeier * inc/util/encoding.hpp: * inc/core/encodingselector.hpp: * inc/dragdrop.hpp: * src/util/encoding.cpp: * src/core/encodingselector.cpp: * src/dragdrop.cpp: Removed. These remained from Gobby 0.4 but were never used in 0.5. * inc/util/Makefile.am: * inc/core/Makefile.am: * inc/Makefile.am: * src/util/Makefile.am: * src/core/Makefile.am: * src/Makefile.am: Removed the files from the build. * inc/window.hpp: Removed includes including those files. 2008-09-18 Armin Burgmeier * inc/dialogs/preferencesdialogs.hpp: * src/dialogs/preferencesdialogs.cpp: Added a security page. * inc/core/browser.hpp: * src/core/browser.cpp: Set discovery's security policy and certificate manager's trust file from preferences. 2008-09-18 Armin Burgmeier * src/core/preferencesdialog.cpp: Use a Gtk::SizeGroup to align the "User name" and "User color" labels correctly. 2008-09-18 Armin Burgmeier * configure.ac: Added a new define, GOBBY_CONFIGDIR, that specifies where Gobby shall store it's configuration files (preferences, known documents, known hosts) * inc/core/preferences.hpp: * src/core/preferences.cpp: Added a security section with trust file and security-policy entries, changed serialise to serialize for consistency. * src/window.cpp: Call preferences.serialize instead of serialise. * src/core/browser.cpp: Use the security policy to create new XMPP connections. Honor GOBBY_CONFIGDIR for known hosts file. * src/core/documentinfostorage: Honor GOBBY_CONFIGDIR for known documents file. * src/main.cpp: Honor GOBBY_CONFIGDIR for config file. 2008-09-17 Armin Burgmeier * src/Makefile.am: * src/commands/Makefile.am: * src/core/Makefile.am: * src/dialogs/Makefile.am: * src/operations/Makefile.am: * src/util/Makefile.am: Removed LOCALE_DIR definition from compiler command line. * configure.ac: Use AM_GLIB_DEFINE_LOCALEDIR instead. * src/main.cpp: Use GOBBY_LOCALEDIR instead of LOCALE_DIR. 2008-09-17 Armin Burgmeier * inc/core/browser.hpp: * src/core/browser.cpp: Instantiate a InfGtkCertificateManager, to handle server certificates. 2008-09-17 Armin Burgmeier * src/commands/view-commands.cpp: Fix usage of an uninitialized variable. 2008-09-13 Armin Burgmeier * src/core/browser.cpp: Adapt to infinote API change. 2008-09-11 Armin Burgmeier * inc/core/closableframe.hpp: * src/core/closableframe.cpp: Added title and icon for the frame. * src/core/docwindow.cpp: * src/window.cpp: Set title and icon for userlist and browser. 2008-09-11 Armin Burgmeier * pixmaps/chat.png: Removed. * pixmaps/Makefile.am: Removed chat.png from distribution. * inc/core/iconmanager.hpp: * src/core/iconmanager.cpp: Removed chat icon. * src/commands/help-commands.cpp: Added Thomas Glatt to artists because he designed the Gobby logo. 2008-09-11 Armin Burgmeier * inc/core/docwindow.hpp: * src/core/docwindow.cpp: Added a boolean parameter to set_info() that specifies whether the info box can be closed by the user. * src/commands/browser-commands.cpp: Make persistent error infos closable, but not temporary synchronization infos. 2008-09-11 Armin Burgmeier * inc/core/closableframe.hpp: * src/core/closableframe.cpp: Added a new widget, derived from Gtk::Frame, that shows a close button in the upper right corner, for the frame to be closed. * inc/core/Makefile.am: * src/core/Makefile.am: Added the new files to the build. * inc/core/preferences.hpp: * src/core/preferences.cpp: Added show_browser and show_userlist options. * inc/window.hpp: * src/window.cpp: * src/core/docwindow.cpp: Use a ClosableFrame for the browser and userlist, respectively. * src/core/browser.cpp: Removed the border_width. It is now applied by the ClosableFrame. If border_width is desired in certain situations, then it can still be called from elsewhere. * inc/core/header.hpp: * src/core/header.cpp: Added "Show Document Browser" and "Show Userlist" items to the View menu. * inc/commands/view-commands.hpp: * src/commands/view-commands.cpp: Implemented functionality for these (by setting the preference options). 2008-09-11 Armin Burgmeier * inc/util/closebutton.hpp: * src/util/closebutton.cpp: Added CloseButton class that provides an as-small-as-possible GtkButton with a close icon on it. * inc/util/Makefile.am: * src/util/Makefile.am: Added new files to the build. * src/core/folder.cpp: Use CloseButton on the tabs. 2008-09-10 Armin Burgmeier * src/core/userlist.cpp: Fixed userlist sorting, sometimes unavailable users were shown above available ones. 2008-09-10 Armin Burgmeier * src/commands/folder-commands.cpp: Fix a warning when the connection to the server has been lost while being subscribed to a document. 2008-09-10 Armin Burgmeier * src/window.cpp: Keep browser pane at a fixed width. 2008-09-10 Armin Burgmeier * inc/core/docwindow.hpp: * src/core/docwindow.cpp: Fixed an endless loop when the userlist is forcefully resized. 2008-09-10 Armin Burgmeier * inc/core/docwindow.hpp: * src/core/docwindow.cpp: Change user color in running session on preferences color change. 2008-09-10 Armin Burgmeier * src/dialogs/preferencesdialog.cpp: Use notify::color instead of signal_color_set on ColorButton, because ColorButton::set_color (as used by HueButton) odes not emit the latter. 2008-09-10 Armin Burgmeier * inc/core/userlist.hpp: * src/core/userlist.cpp: Watch notify::hue instead of notify::color on InfTextUser for user color changes. InfTextUser::color does not exist. 2008-09-09 Armin Burgmeier * inc/core/huebutton.hpp: * src/core/huebutton.cpp: Added HueButton widget which is similar to Gtk::ColorButton but launches a InfTextGtkHueChooser instead of a GtkColorSelector on click. * inc/core/Makefile.am: * src/core/Makefile.am: Add the new files to the build. * inc/dialogs/initialdialog.hpp: * inc/dialogs/preferencesdialog.hpp: * src/dialogs/initialdialog.cpp: * src/dialogs/preferencesdialog.cpp: Use HueButton instead of ColorButton for user color selection. 2008-09-08 Armin Burgmeier * inc/core/header.hpp: * src/core/header.cpp: Added FileClose action and menu item. * inc/commands/file-commands.hpp: * src/commands/file-commands.cpp: Handle it. 2008-09-08 Armin Burgmeier * src/core/folder.cpp: Make tabs reorderable. 2008-09-08 Armin Burgmeier * inc/core/preferences.hpp: * src/core/preferences.cpp: Added userlist_width property. * inc/core/docwindow.hpp: * src/core/docwindow.cpp: Honor it by making sure the userlist always has the width specified in the preferences, and update preferences when the user resizes the user list. * inc/core/folder.hpp: * src/core/folder.cpp: Make Preferences nonconst, so DocWindow can store the current userlist width in it. * src/window.cpp: Increase default window size to 800x600. 2008-09-08 Armin Burgmeier * inc/util/color.hpp: * src/util/color.cpp: Added hue_to_gdk_color(). * inc/core/userlist.hpp: * src/core/userlist.cpp: Added a new widget that displays all users in a session. * inc/core/Makefile.am: * src/core/Makefile.am: Added new files to the build. * inc/core/docwindow.hpp: * src/core/docwindow.cpp: Show userlist in a right pane. * src/core/folder.cpp (remove_document): Keep the session alive until the corresponding page has been removed from the folder. 2008-09-07 Armin Burgmeier * inc/commands/folder-commands.hpp: * src/commands/folder-commands.cpp: Added FolderCommands class, setting user status according to currently activated document in folder. * src/core/folder.cpp: Make sure signal_document_added is emitted before the first emission of signal_document_changed for the new document. * inc/window.hpp: * src/window.cpp: Instantiate FolderCommands. * inc/commands/Makefile.am: * src/commands/Makefile.am: Added new files to the build. 2008-08-27 Armin Burgmeier * src/commands/browser-commands.cpp: Set initial status depending on whether the document is currently displayed in the folder. * src/operations/operation-open.cpp: Set initial status of user in the document to inactive. 2008-08-26 Armin Burgmeier * src/core/noteplugin.cpp: Adapt to libinfinity API change (user_data parameter to InfcNotePlugin callback functions). 2008-08-25 Armin Burgmeier * src/operations/operation-open.cpp: Fixed a typo in a comment. 2008-08-24 Armin Burgmeier * inc/core/nodewatch.hpp: * src/core/nodewatch.cpp: Added a class that watches a node in a InfcBrowser and emits a signal when it is removed. * inc/dialogs/entrydialog.hpp: * src/dialogs/entrydialog.cpp: Added a class that shows a dialog with an entry, to prompt the user for a string to enter. * inc/operations/operation-delete.hpp: * src/operations/operation-delete.cpp: Added an operation that deletes a node. * inc/core/browser.hpp: * src/core/browser.cpp: Added get_view(). * inc/operations/operation-new.hpp: * src/operations/operation-new.cpp: Allow this to create a directory, via a boolean parameter. * inc/operations/operations.hpp: * src/operations/operations.cpp: Added delete_node() and create_directory(). * src/core/documentinfostorage.cpp (on_node_removed): Don't require the removed node to be in our map. * inc/commands/browser-context-commands.hpp: * src/commands/browser-context-commands.cpp: Added a class that shows a context menu for the left browser pane, allowing the user to create or remove nodes. * inc/window.hpp: * src/window.cpp: Instantiate a BrowserContextCommands object. * inc/core/Makefile.am: * inc/dialogs/Makefile.am: * inc/operations/Makefile.am: * inc/commands/Makefile.am: * src/core/Makefile.am: * src/dialogs/Makefile.am: * src/operations/Makefile.am: * src/commands/Makefile.am: Added the new files to the build. 2008-08-24 Armin Burgmeier * inc/core/filechooser.hpp: * src/core/filechooser.cpp: Added a new class that manages a "current folder uri" which specifies at which location the filechooser has been used the last time. This is used as default location for newly opened file chooser dialogs. * inc/core/Makefile.am: * src/core/Makefile.am: Add the new files to the build. * inc/commands/file-commands.hpp: * src/commands/file-commands.cpp: Use this when showing open or save dialogs. * inc/window.hpp: * src/window.cpp: Instantiate a FileChooser, pass to FileCommands. 2008-08-24 Armin Burgmeier * inc/core/browser.hpp: * src/core/browser.cpp: Added the get_selected() and set_selected() methods, using the new inf_gtk_browser_view_set_selected(). * src/commands/browser-commands.cpp: When subscribing to a session, then change the BrowserView selection to the corresponding note, and give focus to the DocWindow, so the user can start typing immediately. 2008-08-14 Armin Burgmeier * src/commands/browser-commands.cpp: Removed an obsolete TODO item. * inc/core/folder.hpp: * src/core/folder.cpp: Automatically record every session to ~/.infinote-records, to be able to reproduce errors that occur somewhere in the session. 2008-08-13 Armin Burgmeier * src/core/header.cpp: Don't show hidden languages for syntax highlighting. 2008-07-23 Armin Burgmeier * src/commands/view-commands.cpp: Block signal_language_changed while closing last document, to avoid a crash in the handler 2008-07-16 Armin Burgmeier * inc/core/header.hpp: * src/core/header.cpp: Added lookup_language_action() to lookup a languagu action by the GtkSourceLanguage. * inc/commands/view-commands.hpp: * src/commands/view-commands.cpp: Switch the language of the current document if the user chooses another language in the menu, and make the menu reflect the language for the current document. * inc/commands/help-commands.hpp: * src/commands/help-commands.cpp: Implemented the About option. * inc/commands/Makefile.am: * src/commands/Makefile.am: Added help-commands to the build. * inc/window.hpp: * src/window.cpp: Instantiate HelpCommands. 2008-07-14 Armin Burgmeier * configure.in: Require giomm 2.16.4 since this contains a bugfix that allows overwriting files when saving. 2008-07-12 Armin Burgmeier * inc/commands/browser-commands.hpp: * src/commands/browser-commands.cpp: Join with an alternative user name if the preferred one is already in use. 2008-07-11 Armin Burgmeier * inc/commands/view-commands.hpp: * src/commands/view-commands.cpp: Implemented View Toolbar and View Statusbar. * inc/commands/Makefile.am: * src/commands/Makefile.am: Add the new files to the build. * inc/core/folder.hpp: * src/core/folder.cpp: Added a const version of get_current_document(). * inc/core/statusbar.hpp: * src/core/statusbar.cpp: Added display of current line and column. * src/core/header.cpp: Hide toolbar initially if the show_toolbar preference is not set. * inc/window.hpp: * src/window.cpp: Instantiate ViewCommands. 2008-07-04 Armin Burgmeier * inc/core/toolwindow.hpp: * src/core/toolwindow.cpp: Removed. ToolWindow is no longer used. * inc/core/Makefile.am: * src/core/Makefile.am: Adjusted build files. 2008-07-04 Armin Burgmeier * inc/dialogs/gotodialog.hpp: * src/dialogs/gotodialog.cpp: Redesigned GotoDialog, and limit maximum line number to the line count of the current document. * inc/commands/edit-commands.hpp: * src/commands/edit-commands.cpp: Show GotoDialog and PreferencesDialog when the corresponding actions have been activated. * inc/dialogs/finddialog.hpp: * src/dialogs/finddialog.cpp: Added a destructor disconnecting from the active_user_changed signal of the current document. * src/core/header.cpp: Added mnemonics for a few menu items. * src/util/file.cpp: Fixed endless recursion in create_directory_with_parents(). 2008-07-03 Armin Burgmeier * inc/commands/edit-commands.hpp: * src/commands/edit-commands.cpp: Take a StatusBar in constructor to hand to find dialog, implemented find and replace. * inc/core/docwindow.hpp: * src/core/docwindow.cpp: Changed set_selection paramaters from const Gtk::TextIter& to const GtkTextIter* since we are using the C API of GtkTextBuffer. * src/dialogs/gotodialog.cpp: Adapt to API change in DocWindow. * inc/dialogs/finddialog.hpp: * src/dialogs/finddialog.cpp: Changed FindDialog to better meet the GNOME HIG, added "Wrap around" option, inherit from Gtk::Dialog instead of Gobby::ToolWindow. * src/core/header.cpp: Add a horizontal ellipsis to all menu items that open dialogs. * src/dialogs/initialdialog.cpp: Call Gtk::Dialog constructor with parent instead of default constructor. * src/window.cpp: Pass statusbar to EditCommands. 2008-07-03 Armin Burgmeier * inc/commands/edit-commands.hpp: * src/commands/edit-commands.cpp: Implemented Cut/Copy/Paste functionality. * src/core/folder.cpp: Emit signal_document_changed when the last document is about to be removed before the document has actually been removed, so that signal handlers that still have a reference to the last document can still make use of it. * src/core/header.cpp: Added a shortcut (L) for Save All, as in gedit. 2008-07-02 Armin Burgmeier * inc/core/docwindow.hpp: * src/core/docwindow.cpp: Added the active_user_changed signal and the get_active_user() function. * src/core/header.cpp: Added shortcuts for Undo and Redo. * src/core/noteplugin.cpp: Disable GtkSourceView Undo/Redo system by calling gtk_source_buffer_begin_non_undoable_action() without ever ending that non-undoable action. * inc/commands/edit-commands.hpp: * src/commands/edit-commands.cpp: New class handling the commands in the Edit submenu. For now Undo and Redo are implemented. * inc/window.hpp: * src/window.cpp: Instantiate EditCommands. * inc/commands/Makefile.am: * src/commands/Makefile.am: Added new files to build. 2008-06-29 Armin Burgmeier * inc/commands/file-commands.hpp: * src/commands/file-commands.cpp: Cleaned up the code a bit, implemented save all. * src/core/folder.cpp: Emit document_changed with NULL argument in case the last document has been removed. 2008-06-28 Armin Burgmeier * pixmaps/save-all.svg: * pixmaps/Makefile.am: Added icon for the save all menu item, contributed by Benjamin Herr * inc/core/iconmanager.hpp: * src/core/iconmanager.cpp: Add it to the icon manager. * src/core/header.cpp: Add it to the menu. * src/commands/file-commands.cpp: Fix a crash when unsubscribing from a session after having opened the file chooser to save a file. 2008-06-28 Armin Burgmeier * inc/core/documentinfostorage.hpp: * src/core/documentinfostorage.cpp: Moved from operations/documentinfostorage.[hc]pp. Also added the get_key() method and added overloads of get_info and set_info that take a key instead of browser and iter. * inc/operations/operation-open.hpp: Fixed include of documentinfostorage.hpp. * inc/operations/operation-save.hpp: * src/operations/operation-save.cpp: Implemented saving of documents. This currently does refuse to overwrite existing files due to a bug in giomm, see http://bugzilla.gnome.org/show_bug.cgi?id=540656. * inc/core/docwindow.hpp: * src/core/docwindow.cpp: Store a storage info key, so that on save the corresponding info can be updated. * inc/core/folder.hpp: * src/core/folder.cpp: Pass a correct storage info key to newly created documents. * inc/commands/browser-commands.hpp: * src/commands/browser-commands.cpp: Pass a storage info key to the folder when a new document is opened because of a new subscription. * inc/commands/file-commands.hpp: * src/commands/file-commands.cpp: Implemented save and save as functionality. * inc/operations/operations.hpp: * src/operations/operations.cpp: Added save_document(). * src/window.cpp: Pass info storage to browser and file commands. * inc/core/Makefile.am: * inc/operations/Makefile.am: * src/core/Makefile.am: * src/operations/Makefile.am: Add new and moved files to build. 2008-06-28 Armin Burgmeier * inc/commands/browser-commands.hpp: * src/commands/browser-commands.cpp: Set info text in DocWindow and make the document readonly when the connection to the publisher has been lost. * src/core/docwindow.cpp: Allow user to be NULL in set_active_user(). * src/core/folder.cpp: Set icon in tab label when the connection to the document's publisher has been lost. 2008-06-27 Armin Burgmeier * inc/commands/browser-commands.hpp: * src/commands/browser-commands.cpp: Make sure subscription progress is only shown for sessions that are synchronized, not for sync-ins. * inc/core/preferences.hpp: Removed the non-const operator Type& since it allows changing the Properties content without notification. Also added get() in analogy to set(). * inc/operations/operation-open.hpp: * src/operations/operation-open.cpp: Take a Preferences in the constructor, add a user to the session before synchronizing it to the server so that we can start editing the document before the synchronization is complete. * inc/operations/operations.hpp: * src/operations/operations.cpp: * src/window.cpp: Pass the Preferences object to OperationOpen. * src/operations/operation-new.cpp: Automatically Subscribe to newly created documents. 2008-06-27 Armin Burgmeier * inc/commands/browser-commands.hpp: * src/commands/browser-commands.cpp: Watch synchronization and join user for every session subscribed to, not only sessions that have been subscribed to by the BrowserCommands. * src/core/folder.cpp: Fixed a typo in a comment 2008-06-25 Yarek Tyshchenko (Merged from master): * configure.ac: Require ige-mac-integration when Gobby is compiled natively on OS X. * src/core/header.cpp: Integrate the menu bar into the main OS X menu bar. 2008-06-22 Armin Burgmeier * src/operations/documentinfostorage.cpp: Added return value for load_document, fixed storage of "root" node (was in top-level instead of ), * src/operations/operation-open.cpp: Added a TODO comment to remmove memmove. 2008-06-22 Armin Burgmeier * inc/operations/documentinfostorage.hpp: * src/operations/documentinfostorage.cpp: Disconnect from the set-browser signal handler on destruction. 2008-06-21 Armin Burgmeier * inc/util/file.hpp: * src/util/file.cpp: * inc/util/Makefile.am: * src/util/Makefile.am: Added file.hpp/.cpp containing utility functions to deal with the filesystem. Currently, a function that creates a directory and all its parent if necessary is implemented. * inc/util/config.hpp: * src/util/config.cpp: Make use of that function in the destructor. * inc/util/resolv.hpp: Include since we use std::auto_ptr<>. * inc/operations/documentinfostorage.hpp: * src/operations/documentinfostorage.cpp: Added class that stores local information on known documents such as the local location, the encoding and the eol style. * inc/operations/operation-open.hpp: * src/operations/operation-open.cpp: Implemented opening documents from files, via giomm. * inc/operations/Makefile.am: * src/operations/Makefile.am: Add the new source files to the build. * inc/operations/operations.hpp: * src/operations/operations.cpp: Added encoding parameter to create_document(), added DocumentInfoStorage member. * inc/commands/file-commands.hpp: * src/commands/file-commands.cpp: Added functionlity for the Open menu item. * inc/window.hpp: * src/window.cpp: Instantiate a DocumentInfoStorage, pass to Operations. * src/main.cpp: Initialize giomm. * configure.ac: Added giomm as a dependency. 2008-05-19 Armin Burgmeier * inc/Makefile.am: * inc/operations/Makefile.am: * inc/operations/operation-new.hpp: * inc/operations/operations.hpp: * src/Makefile.am: * src/operations/Makefile.am: * src/operations/operation-new.cpp: * src/operations/operations.cpp: Added Operation subsystem, handling tracking of asynchronous operations. * inc/commands/file-commands.hpp: * src/commands/file-commands.cpp: Make use of OperationNew to create new documents. * inc/window.hpp: * src/window.cpp: Instantate an Operations object, pass to FileCommands. * configure.ac: * Makefile.am: Create new makefiles, link libgobby-operations.h. 2008-05-18 Armin Burgmeier * inc/core/Makefile.am: * src/core/Makefile.am: * inc/core/noteplugin.hpp: * src/core/noteplugin.cpp: Added text plugin as a public global constant. * src/window.cpp: Adapt. 2008-05-18 Armin Burgmeier * src/dialogs/documentlocationdialog.cpp: Initially refilter the InfGtkBrowserModelFilter, otherwise previously explored nodes are always visible. * src/commands/file-commands.cpp: Adapt to the new infinote API in infc_browser_add_note(). 2008-05-09 Armin Burgmeier * inc/dialogs/documentlocationdialog.hpp: * src/dialogs/documentlocationdialog.cpp: Only allow ACCEPT response when a subdirectory is selected, filter all nodes but subdirectories. * src/commands/file-commands.cpp: Hide DocumentLocationDialog after usage. 2008-05-09 Armin Burgmeier * inc/util/Makefile.am: * inc/util/color.hpp: * inc/util/config.hpp: * inc/util/defaultaccumulator.hpp: * inc/util/encoding.hpp: * inc/util/i18n.hpp: * inc/util/resolv.hpp: * inc/util/serialize.hpp: * inc/core/Makefile.am: * inc/core/browser.hpp: * inc/core/docwindow.hpp: * inc/core/encodingselector.hpp: * inc/core/folder.hpp: * inc/core/header.hpp: * inc/core/iconmanager.hpp: * inc/core/preferences.hpp: * inc/core/statusbar.hpp: * inc/core/toolwindow.hpp: * inc/dialogs/Makefile.am: * inc/dialogs/finddialog.hpp: * inc/dialogs/gotodialog.hpp: * inc/dialogs/initialdialog.hpp: * inc/dialogs/preferencesdialog.hpp: * inc/commands/Makefile.am: * inc/commands/browser-commands.hpp: * inc/window.hpp: * inc/Makefile.am: * src/util/Makefile.am: * src/util/color.cpp: * src/util/config.cpp: * src/util/encoding.cpp: * src/util/i18n.cpp: * src/util/resolv.cpp: * src/util/serialize.cpp: * src/core/Makefile.am: * src/core/browser.cpp: * src/core/docwindow.cpp: * src/core/encodingselector.cpp: * src/core/folder.cpp: * src/core/header.cpp: * src/core/iconmanager.cpp: * src/core/preferences.cpp: * src/core/statusbar.cpp: * src/core/toolwindow.cpp: * src/dialogs/Makefile.am: * src/dialogs/finddialog.cpp: * src/dialogs/gotodialog.cpp: * src/dialogs/initialdialog.cpp: * src/dialogs/preferencesdialog.cpp: * src/commands/Makefile.am: * src/commands/browser-commands.cpp: * src/dragdrop.cpp: * src/main.cpp: * src/window.cpp: * src/Makefile.am: * Makefile.am: * configure.ac: Reordered file hierarchy, fixing includes. * inc/dialogs/documentlocationdialog.hpp: * src/dialogs/documentlocationdialog.cpp: Added a dialog allowing the user to select on which directory and server to create a document. * inc/commands/file-commands.hpp: * src/commands/file-commands.cpp: Added functionality for File/New using the new dialog. 2008-04-20 Armin Burgmeier * inc/browser.hpp: * src/browser.cpp: Adapt to new infinote API by using InfGtkBrowserStore instead of InfGtkBrowserModel. 2008-04-20 Armin Burgmeier * src/browser.cpp: Fixed a format string (%1 instead of %0). 2008-04-13 Armin Burgmeier * inc/docwindow.hpp: * src/docwindow.cpp: Added DocWindow::set_active_user(). * src/folder.cpp: Show icon in the tab representing the current subscription state. * src/commands/browser-commands.cpp: Set active user after user join. 2008-04-13 Armin Burgmeier * inc/commands/browser-commands.hpp: * src/commands/browser-commands.cpp: Track synchronization, perform a user join after synchronization, show error and status messages. * inc/docwindow.hpp: * src/docwindow.cpp: Implemented DocWindow::set_info() and DocWindow::unset_info(). * src/folder.cpp: Changed Folder::lookup_document() to use a PageList and an iterator instead of traversing the list by index. * src/window.cpp: Pass the preferences to the BrowserCommands because it needs to find the name for the user to join. 2008-04-12 Armin Burgmeier * inc/commands/browser-commands.hpp: * inc/commands/Makefile.am * src/commands/browser-commands.cpp: * src/commands/Makefile.am: Added functionality to subscribe to a session. * inc/Makefile.am: * src/Makefile.am: Recurse into the commands subdirectory. * Makefile.am: Link src/commands/libgobby-commands.a * configure.ac: Create inc/commands/Makefile.am and src/commands/Makefile.am. * inc/folder.hpp: * src/folder.cpp: Added switch_to_document(), make close button in tab label look correct. * src/header.cpp: Rename label for save all menu item from "Save all" to "Save All". * src/docwindow.cpp: Make the window non-editable by default. We make it editable later when a local user has joined. * inc/window.hpp: * src/window.cpp: Added BrowserCommands object. 2008-04-12 Armin Burgmeier * contrib/gobby-0.0.20050522.ebuild: * contrib/artwork/chat.svg: * test/test_document.cpp: Removed, these are outdated. * inc/default_accumulator.hpp: * inc/serialize.hpp: * src/serialize.cpp: Added. These are basically copies from net6, but in the Gobby namespace. * inc/i18n.hpp: * src/i18n.cpp: Moved from inc/common.hpp or src/common.cpp, respectively. * inc/application_state.hpp: * inc/buffer_def.hpp: * inc/chat.hpp: * inc/colorsel.hpp: * inc/historyentry.hpp: * inc/document.hpp: * inc/document_settings.hpp: * inc/entrydialog.hpp: * inc/fileentry.hpp: * inc/gselector.hpp: * inc/hostdialog.hpp: * inc/hostprogressdialog.hpp: * inc/ipc.hpp: * inc/joindialog.hpp: * inc/joinprogressdialog.hpp: * inc/logview.hpp: * inc/passworddialog.hpp: * inc/progressdialog.hpp: * inc/regex.hpp: * inc/togglewindow.hpp: * inc/unix.hpp: * src/application_state.cpp: * src/buffer_def.cpp: * src/chat.cpp: * src/colorsel.cpp: * src/document.cpp: * src/document_settings.cpp: * src/entrydialog.cpp: * src/fileentry.cpp: * src/gselector.cpp: * src/historyentry.cpp: * src/hostdialog.cpp: * src/hostprogressdialog.cpp: * src/ipc.cpp: * src/joindialog.cpp: * src/joinprogressdialog.cpp: * src/logview.cpp: * src/passworddialog.cpp: * src/preferencesdialog.cpp: * src/progressdialog.cpp: * src/regex.cpp: * src/togglewindow.cpp: * src/unix.cpp: Removed. They are no longer required for infinote, or at least not in a such way that a complete rewrite would not be easier anyway. * inc/config.hpp: * inc/dragdrop.hpp: * inc/encoding.hpp: * inc/finddialog.hpp: * inc/folder.hpp: * inc/statusbar.hpp: * inc/window.hpp: * src/browser.cpp: * src/config.cpp: * src/dragdrop.cpp: * src/encoding.cpp: * src/encoding_selector.cpp: * src/finddialog.cpp: * src/folder.cpp: * src/gotodialog.cpp: * src/header.cpp: * src/icon.cpp: * src/initialdialog.cpp: * src/main.cpp: * src/resolv.cpp: * src/statusbar.cpp: * src/window.cpp: Removed any occurences of net6/obby stuff, replaced by equivalent functionality if possible. * po/POTFILES.in: Removed source files, added a TODO comment to readd them as soon as the required files are ready. * Makefile.am: * contrib/Makefile.am: * contrib/artwork/Makefile.am: * inc/Makefile.am: * pixmaps/Makefile.am: * src/Makefile.am: * win32/Makefile.am: Hierarchical build instead of only a top-level Makefile.am, so that each Makefile.am is smaller and easier to use. * configure.ac: Remove dependency on net6 and obby. 2008-04-12 Armin Burgmeier * src/preferences.cpp: Changed default value for homeend_smart to false. * inc/resolv.hpp: * src/resolv.cpp: Changed signature of callbacks to include the ResolvHandle. * inc/browser.hpp: * src/browser.cpp: Added resolv map to correctly cancel pending resolvs on destruction. * inc/statusbar.hpp: * src/statusbar.cpp: Correctly remove the timeout connection on destruction of a statusbar message. * inc/folder.hpp: * inc/docwindow.hpp: * src/folder.cpp: * src/docwindow.cpp: Changed Folder and DocWindow to be used with InfTextSession instead of obby::local_document_info. * inc/window.hpp: * src/finddialog.cpp: * src/gotodialog.cpp: * src/window.cpp: Adapted to Folder and DocWindow changes. 2008-03-13 Armin Burgmeier * inc/statusbar.hpp: * src/statusbar.cpp: Removed old functionality, add methods to add and remove status messages. * inc/window.hpp: * inc/window.cpp: Adapt. 2008-03-09 Armin Burgmeier * inc/documentlist.hpp: * inc/userlist.hpp: * src/documentlist.cpp: * src/userlist.cpp: * Makefile.am: Removed. * inc/preferences.hpp: * src/preferences.cpp: Added show_toolbar and show_statusbar preferences. * inc/initialdialog.hpp: Added missing include (gtkmm/image.h). * inc/header.hpp: * src/header.cpp: Redesign menu/toolbar to better match common editors. * inc/window.hpp: * src/folder.cpp: * src/window.cpp: Adapt to above changes, current UI disfunctional. 2008-03-06 Armin Burgmeier * inc/resolv.hpp: * src/resolv.cpp: Code to asynchronously resolve host names. * inc/browser.hpp: * src/browser.cpp: Added Browser class that shows remote directories to browse (via InfGtkBrowserView), and that allows to create direct connections. * inc/window.hpp: * src/window.cpp: Show browser in a sidepane to the left, remove chat. * src/initialdialog.cpp: Set window title. * Makefile.am: Added browser.{h,c}pp and resolv.{h,c}pp * configure.ac: Depend on infinote. 2008-03-03 Armin Burgmeier * inc/initialdialog.hpp: * src/initialdialog.cpp: * Makefile.am: Added an initial welcome dialog. * inc/window.hpp: * src/window.cpp: Show initial dialog the first time gobby is started. 2008-03-03 Armin Burgmeier * inc/preferencesdialog.hpp: * src/preferencesdialog.cpp: Store chosen preferences. * inc/preferences.hpp: Add Preferences::Option::set for convenience. * inc/colorutil.hpp: * src/colorutil.cpp: * Makefile.am: Added colorutil containing some utility functions to handle colors. 2008-03-02 Armin Burgmeier * configure.ac: Require at least gtkmm 2.12, drop gtksourceview1 support, post-branch bump to 0.4.90. * inc/preferences.hpp: * src/preferences.cpp: Removed options not required for infinote, added user preferences page. * inc/preferencesdialog.hpp: * src/preferencesdialog.cpp: Adapted and redesigned preferences dialog to better meet the Gnome HIG. Currently disfunctional. * inc/header.hpp: * inc/folder.hpp: * inc/docwindow.hpp: * inc/window.hpp: * src/header.cpp: * src/folder.cpp: * src/chat.cpp: * src/togglewindow.cpp: * src/docwindow.cpp: * src/window.cpp: Adapt. ChangeLog.pre-0.5000066400000000000000000001715571304552132100137310ustar00rootroot000000000000002008-01-31 Armin Burgmeier * src/config.cpp: File test before loading XML file, this seems to crash on Windows otherwise. 2008-01-24 Philipp Kern * src/header.cpp: * src/preferences.cpp: * src/preferencesdialog.cpp: * src/unix.cpp: Fix build failures with g++-4.3 (thanks to Matthias Klose) 2008-01-20 Armin Burgmeier * src/preferences.cpp: * src/preferencesdialog.cpp: * src/header.cpp: Don't crash when no GtkSourceView languages are installed. 2008-01-03 Philipp Kern * contrib/gobby.desktop: adhere to the freedesktop.org Desktop Entry Specification v1.0 2008-01-01 Armin Burgmeier * src/preferencesdialog.cpp: Add some spacing to the document management option group. 2008-01-01 Philipp Kern * configure.ac: bump version to 0.4.6 * NEWS: documented the changes 2008-01-01 Philipp Kern * po/pt_BR.po, po/pl.po, po/ko.po: added new translations from Rosetta * po/*.po: translation roundup fetched from Rosetta * configure.ac: add pt_BR, pl and ko 2008-01-01 Philipp Kern * configure.ac: bump obby dependency to 0.4.5 to get IPv6 autodiscovery 2007-12-16 Armin Burgmeier * src/toolwindow.hpp: * src/toolwindow.cpp: Hide on escape. 2007-11-27 Armin Burgmeier * src/docwindow.cpp: Enable indentation-on-tab in gtksourceview1 * configure.in: Require gtksourceview >= 1.8. 2007-10-14 Armin Burgmeier * inc/preferencesdialog.hpp: * src/preferencesdialog.cpp: Don't use the tooltip for the smart home/end option when gtkmm was built without deprecated API. * src/logview.cpp: Replaced scroll_to_mark by scroll_to because the former is deprecated. * src/docwindow.cpp: Fix gtksourceview1 build. 2007-09-30 Benjamin Herr * src/window.cpp: Make sure that the two buttons on the confirmation dialog when exiting gobby with a session running do not have the same mnemonics. 2007-09-20 Philipp Kern * src/ipc.cpp: added unistd.h to the list of includes to ease porting to OpenBSD 2007-09-17 Armin Burgmeier * src/preferencesdialog.cpp: * src/header.cpp: * src/docwindow.cpp: * src/preferences.cpp: Adapted to latest (and final) GtkSourceView2 API. 2007-08-27 Armin Burgmeier * src/historyentry.cpp: Avoid Gtk::Entry::set_text() because it seems to crash on Vista. Use Gtk::Editable::delete_text and Gtk::Editable::insert_text instead. 2007-08-27 Armin Burgmeier * src/window.cpp: Make sure not to try to send an unsubscription request twice. 2007-08-18 Armin Burgmeier * src/icon.cpp: Win32 build fix. 2007-08-18 Philipp Kern * inc/window.hpp: * src/window.cpp: do not close document if `save as' is cancelled [fixes #288] 2007-08-18 Philipp Kern * NEWS: * configure.ac: bump version to 0.4.5 and document the changes 2007-08-12 Armin Burgmeier * src/window.cpp: Set IO channel encoding to "" when saving files because we do the necessary conversion ourselves before saving. This fixes saving non-UTF-8 encoded files. * src/icon.cpp: Convert pixmap paths to UTF-8 on windows, allowing gobby to run inside paths containing non-ASCII characters. * src/preferences.cpp: Include features.hpp for correct gtksourceview2 detection. 2007-08-05 Philipp Kern * Place a `Save' button instead of a plain `OK' button in the `Save file' dialog and set it as the default response. 2007-07-22 Philipp Kern * Fix compilation with GtkSourceView 1 2007-07-22 Philipp Kern * New Swedish translation by Daniel Nylander [fixes #282] 2007-07-09 Philipp Kern * src/main.cpp: bugfix to use IPC by default 2007-07-09 Philipp Kern * inc/joindialog.hpp: * src/joindialog.cpp: add support for IPv6 autodiscovery 2007-07-09 Armin Burgmeier * src/preferencesdialog.cpp: * src/header.cpp: * src/docwindow.cpp: * src/preferences.cpp: Adjusted for gtksourceview2 API changes. 2007-06-16 Philipp Kern * configure.ac: fixed POSIX compliance 2007-06-16 Philipp Kern * po/POTFILES.in: add more source files to be scanned for strings to be translated * po/gobby.pot: update, mainly line numbers * po/ca.po: * po/pt.po: * po/es.po: * po/fr.po: * po/de.po: * po/sv.po: * po/zh_CN.po: translation roundup: new line numbers, some strings changed by Rosetta contributors * configure.ac: * po/it.po: * po/ru.po: new translations imported from Rosetta * NEWS: documented new translations 2007-06-15 Philipp Kern * NEWS: * configure.ac: bump version to 0.4.4 and document the changes * mkinstalldirs: removed from the repository, is symlinked through autogen.sh 2007-06-15 Philipp Kern * src/finddialog.cpp: * src/window.cpp: * src/statusbar.cpp: * src/folder.cpp: * src/document.cpp: * src/preferencesdialog.cpp: * src/main.cpp: * src/header.cpp: * src/docwindow.cpp: * src/preferences.cpp: * inc/window.hpp: * inc/document.hpp: * inc/header.hpp: * inc/docwindow.hpp: * inc/preferences.hpp: * inc/documentlist.hpp: fixed the indentation from two spaces to one tab and removed some cruft 2007-06-13 Philipp Kern * src/chat.cpp: use Gtk::Entry::delete_text(...) instead of Gtk::Entry::set_text(""); the latter crashes on Windows Vista 2007-06-12 Armin Burgmeier * src/documentlist.cpp: fixed a bug related to the document list's selection (clear the selection in obby_start) [fixes #271] 2007-05-27 Armin Burgmeier * inc/sourceview: * src/sourceview: * Makefile.am: Remove hand-coded GtkSourceView wrappers. * configure.ac: Added --with-gtksourceview2 configure option. * src/finddialog.cpp: * src/window.cpp: * src/statusbar.cpp: * src/folder.cpp: * src/document.cpp: * src/preferencesdialog.cpp: * src/main.cpp: * src/header.cpp: * src/docwindow.cpp: * src/preferences.cpp: * src/gotodialog.cpp: * inc/window.hpp: * inc/folder.hpp: * inc/document.hpp: * inc/preferencesdialog.hpp: * inc/header.hpp: * inc/docwindow.hpp: * inc/preferences.hpp: Support GtkSourceview2, use GtkSourceView C API directly. 2007-05-10 Benjamin Herr * inc/documentlist.hpp: * src/documentlist.cpp: sort document list alphabetically 2007-04-23 Philipp Kern * src/togglewindow.cpp: ToggleWindow needs UTILITY hints in any case * src/toolwindow.cpp: use DIALOG unconditionally on every ToolWindow 2007-04-23 Philipp Kern * src/toolwindow.cpp: use DIALOG hint on *nix, UTILITY on Win32 * src/window.cpp: copyright notice renewed 2007-04-16 Armin Burgmeier * src/chat.cpp: Reintroduce user tags in chat which got accidentaly lost in revision [1515]. 2007-04-15 Armin Burgmeier * src/joindialog.cpp: Don't allow selection in the error case, use a single column for both icon and text. Make the dialog resizable. 2007-04-15 Philipp Kern * src/joindialog.cpp: failure handling for exceptions thrown by discover 2007-04-14 Armin Burgmeier * src/joindialog.cpp: Only access the currently selected row to set the content of the host and port entries when there actually is a selected row. 2007-04-14 Armin Burgmeier * inc/joindialog.hpp: * src/joindialog.cpp: Take a obby::zeroconf_base instead of obby::zeroconf, do not install timer when using avahi as zeroconf backend. * inc/window.hpp: * src/window.cpp: Use Glib mainloop for avahi zeroconf backend, otherwise poll it every 1500 milliseconds. 2007-04-14 Philipp Kern * configure.ac, Makefile.am: check for Avahi in obby and link against avahi-glib if Avahi proper is used for Zeroconf support; provide an additional define WITH_AVAHI for usage within the code 2007-04-09 Armin Burgmeier * inc/folder.hpp: * src/folder.cpp: Added a select_document() function that selects the page that shows the given document. * src/userlist.cpp: * src/documentlist.cpp: Make use of it. 2007-04-09 Philipp Kern * src/window.cpp: * inc/documentlist.hpp: * src/documentlist.cpp: allow the same in the document list 2007-04-09 Philipp Kern * inc/window.hpp: * src/window.cpp: pass a folder object to the user list * inc/userlist.hpp: * src/userlist.cpp: doubleclicks on already subscribed documents now cause the document to be selected 2007-04-09 Philipp Kern * configure.ac: bump version to 0.4.3 * NEWS: note down usability improvements in 0.4.3 2007-04-09 Philipp Kern * src/window.cpp: do not show tabs if only one document present [fixes #220] 2007-04-08 Philipp Kern * src/preferencesdialog.cpp: add toolbar setting for "text besides icon" * src/header.cpp: set important flags of the actions according to the sensitivity 2007-04-08 Philipp Kern * src/window.cpp: * src/chat.cpp: set urgency hint on window only when chat widget is visible [fixes #256] 2007-04-07 Philipp Kern * inc/userlist.hpp: * src/userlist.hpp: allow doubleclicks on documents in the userlist to subscribe to them [fixes #225] 2007-04-07 Philipp Kern * inc/document_settings.hpp: signals now pass a non-const LocalDocumentInfo reference * inc/window.hpp: * src/window.cpp: automatically opened tabs should not grab the focus [finally fixes #232] 2007-04-07 Philipp Kern * inc/document_settings.hpp: * src/document_settings.cpp: add a flag whether the document was opened automatically or not 2007-04-02 Philipp Kern * src/window.cpp: * inc/preferences.hpp: * src/preferences.cpp: * inc/preferencesdialog.hpp: * src/preferencesdialog.cpp: add new configuration option to open new remotely-created documents automatically [fixes #232] 2007-04-02 Philipp Kern * src/window.cpp: Mention avahi-daemon in failure message. 2007-03-27 Philipp Kern * src/window.cpp: Output an error to the console if the zeroconf daemon is unavailable instead of displaying an annoying message box. 2007-03-27 Philipp Kern * src/preferences.cpp: Fix some mime types. It seems that they are inconsistent over different distributions, at least the fixed ones are commonly changed. 2007-03-24 Armin Burgmeier * inc/window.hpp: * src/window.cpp: Focus find and goto dialogs always when the corresponding actions are triggered. Also do not load them at startup but as soon as they are required, to improve startup time [fixes #251]. 2007-03-21 Armin Burgmeier * src/chat.cpp: Do not print an incoming message as much times as its number of lines, but only once. 2007-02-25 Armin Burgmeier * src/window.cpp: Open output file to save a document with Glib::IOChannel instead of std::ofstream to get correct conversion of filename encoding from glib. 2007-02-25 Armin Burgmeier * src/ipc.cpp: Define _WIN32_WINNT to get HWND_MESSAGE API. 2007-02-25 Philipp Kern * NEWS, configure.in: Bump version to 0.4.2 and document important changes. 2007-01-02 Philipp Kern Moved GlobalUnlock further downwards (The pointer is only guaranteed to point to the right place within GlobalLock and GlobalUnlock, thus it might point to an invalid memory block. [Michael Walter]) 2006-12-31 Armin Burgmeier Accept absolute file names for IPC [fixes #237] 2006-11-08 Armin Burgmeier Keep viewport position on remote operations 2006-11-04 Armin Burgmeier Allow subscribing by double click on document list 2006-11-04 Armin Burgmeier Libtool stuff 2006-11-04 Armin Burgmeier Do not set modified flag when opening a local file [fixes #214] 2006-10-26 Philipp Kern Put the urgency hint into the preferences dialog 2006-09-05 Philipp Kern MingW compilation fix: windres now detected by autoconf 2006-09-05 Philipp Kern MingW compilation fix 2006-08-27 Philipp Kern Updated ChangeLog for release 2006-08-27 Philipp Kern Bump version to 0.4.1 2006-08-21 Armin Burgmeier Load pixmaps from pixmaps/ if not found in PIXMAPS_DIR 2006-08-21 Philipp Kern Move the PNGs out of contrib/artwork into pixmaps 2006-08-21 Philipp Kern Remove inline pixmaps and use the external graphics in any case 2006-08-17 Philipp Kern Removed Gobby::Atomic, no longer needed 2006-08-17 Philipp Kern Translation roundup 2006-08-17 Philipp Kern Updated the manual page 2006-08-16 Philipp Kern Bump version to 0.4.0rc4 and depend on obby-0.4.0rc4 or higher 2006-08-16 Armin Burgmeier Preselect line in GotoDialog 2006-08-16 Armin Burgmeier Do not allow self-highlighting 2006-08-15 Philipp Kern Updated NEWS 2006-08-14 Philipp Kern Moved the contents of the View menu to Edit 2006-08-14 Armin Burgmeier Set type hint of tool windows to UTILITY 2006-08-11 Armin Burgmeier Renamed disable-ipc to new-instance 2006-08-08 Armin Burgmeier Made statusbar more standard-compliant 2006-08-08 Armin Burgmeier Added --join and --disable-ipc command line options 2006-08-08 Armin Burgmeier Set urgency hint when chat messages arrive 2006-08-08 Philipp Kern Save user-modified palettes in the configuration [fixes #181] 2006-08-07 Philipp Kern Updated ChangeLog for release 2006-08-07 Philipp Kern Bumped version to 0.4.0rc3 2006-08-07 Philipp Kern Strictened the build-dependency on obby-0.4.0rc3 2006-06-16 Philipp Kern GCC 3.3 compile fixes 2006-06-15 Philipp Kern Do not crash when threads are already initialised 2006-06-07 Philipp Kern Replace "Shows up" by "Displays" 2006-06-07 Philipp Kern Updated ChangeLog for release 2006-06-06 Philipp Kern Depend on obby >= 0.4.0rc2; bump version to 0.4.0rc2 2006-06-07 Armin Burgmeier Fix SEGV caused by wrong usage of Gtk::AccelKey [fixes #163] 2006-06-04 Philipp Kern Translation roundup; new Catalan translation, thanks to Jordi Mallach 2006-06-04 Philipp Kern Bumped gobby.pot to reflect the current source code locations 2006-05-07 Philipp Kern Add document test to the `make check' testrunner 2006-05-07 Armin Burgmeier Added append tests 2006-05-07 Armin Burgmeier Added support for obby::text::npos to Document::erase and Document::get_slice 2006-05-07 Armin Burgmeier Added Document test 2006-05-07 Armin Burgmeier Added Document::clear() 2006-05-06 Philipp Kern Check for substrings in Win32 detection 2006-04-24 Armin Burgmeier Added contrib/artwork/doclist.png to EXTRA_DIST [fixes #152] 2006-04-19 Philipp Kern Add a man page for Gobby 2006-04-16 Philipp Kern Do not depend on JoinDialog creation on invokation 2006-04-15 Philipp Kern Check for a generic zeroconf target in obby 2006-04-15 Philipp Kern Expand the artists' credits 2006-04-11 Philipp Kern Updated ChangeLog for release 2006-04-10 Armin Burgmeier src/gselector.cpp: Added idle call in timout handler. This is a workaround for a possible bug in glib: A connection to Glib::signal_io is not triggered without waking up the main loop once more. 2006-04-10 Philipp Kern Enable URL support on Mac OS X 2006-04-10 Philipp Kern Updates to INSTALL 2006-04-10 Philipp Kern Little wrapping issue in translation 2006-04-10 Philipp Kern Reorder the PKG_CHECK_MODULES calls 2006-04-09 Armin Burgmeier Use Gobby's color selection palette in JoinProgressDialog color prompt 2006-04-09 Armin Burgmeier Fixed a thread-related issue that could cause a SEGV on connection loss 2006-04-01 Armin Burgmeier Inherit Document from sigc::trackable 2006-03-31 Philipp Kern gtk-connect does not work on buttons on Mac; reason unknown 2006-03-31 Philipp Kern Update version to 0.4.0rc1 2006-03-31 Philipp Kern Some kind of translation roundup, very limited 2006-03-30 Philipp Kern Empty the TODO for now 2006-03-31 Philipp Kern Updated README 2006-03-30 Armin Burgmeier Align info labels in preferences dialog to the left 2006-03-30 Armin Burgmeier Set priority of user tags lower than GtkSourceView's bracket highlighting tag 2006-03-30 Armin Burgmeier Initialize gnome-vfs correctly 2006-03-30 Armin Burgmeier Link http URLs in LogView when compiled --with-gnome [fixes #80] 2006-03-30 Philipp Kern Include the stat header 2006-03-30 Armin Burgmeier Fixed config class 2006-03-29 Armin Burgmeier Fixed Config::ParentEntry::supply_value 2006-03-29 Philipp Kern Depend on gnome-vfs-2.0 when compiled with GNOME support 2006-03-29 Armin Burgmeier Refactored Config class 2006-03-28 Armin Burgmeier Print remove command errors to chat 2006-03-28 Armin Burgmeier Use default context in ipc.cpp 2006-03-28 Armin Burgmeier Avoid unnecessary compiler warnings 2006-03-27 Armin Burgmeier Added timeout capability to GSelector, enable keepalives [fixes #131] 2006-03-25 Armin Burgmeier Added document list item 2006-03-25 Armin Burgmeier Remember position of tool windows 2006-03-25 Armin Burgmeier set initially given font name in Font::set when font selector is never realized 2006-03-25 Armin Burgmeier Send command line parameters only to other gobbys owned by the same user 2006-03-25 Armin Burgmeier Win32 IPC using hidden windows 2006-03-25 Armin Burgmeier Changed another occurence of obby-0.3 in configure script 2006-03-25 Philipp Kern Depend on net6-1.3 and obby-0.4 2006-03-25 Armin Burgmeier Pass command line parameters to remote Gobby instanc 2006-03-24 Philipp Kern Bump version to 0.3.99 to avoid confusion with final versions 2006-03-24 Armin Burgmeier Do not explictely request encryption, this is done by obby 2006-03-24 Armin Burgmeier Pack statusbar frames without space 2006-03-24 Armin Burgmeier Some fixes to JoinProgressDialog 2006-03-24 Philipp Kern Cosmetic changes to remove the references to RSA 2006-02-08 Benjamin Herr fixed possible out-of-bounds condition in convert2unix 2006-03-20 Armin Burgmeier Protect GSelector by a mutex 2006-03-17 Philipp Kern Initial NEWS section for 0.4.0 2006-03-19 Armin Burgmeier Send chat messages beginning with '/' as command 2006-03-19 Armin Burgmeier Activate goto button when pressing enter 2006-03-17 Philipp Kern We're in 2006 2006-03-17 Armin Burgmeier Reenabled user color change (has been dropped accidently) 2006-03-17 Armin Burgmeier Replaced DefaultDialog by Gtk::Dialog::set_default_response [fixes #117] 2006-03-15 Armin Burgmeier Use Gtk::Statusbar instead of Gtk::Frame for status bar 2006-03-15 Armin Burgmeier Added toggle button to hide chat [fixes #134] 2006-03-15 Armin Burgmeier Added chat icon by Benjamin Herr 2006-03-15 Armin Burgmeier Added font selection to preferences [fixes #123] 2006-03-14 Armin Burgmeier Implemented EncodingSelector::remove_text for compatibility with gtkmm < 2.8 2006-03-14 Armin Burgmeier Reopen host/join dialog after hosting/joining failed [fixes #130] 2006-03-10 Armin Burgmeier Show suffixed name in window title and document list [fixes #111] 2006-03-10 Armin Burgmeier Removed "Connection is now encrypted" debug message 2006-03-10 Armin Burgmeier Hide mime type column in file preferences page 2006-03-10 Armin Burgmeier Added mnemonics to main menu bar 2006-03-10 Armin Burgmeier Removed remaining references to mimemap 2006-03-09 Armin Burgmeier Ctrl+Alt+PgDown/Up shifts document [fixes #112] 2006-03-09 Armin Burgmeier Added filelist to preferences 2006-03-08 Armin Burgmeier Replaced mime map by file list in preferences 2006-03-07 Armin Burgmeier Disable OK button in password dialog when passwords do not match 2006-03-07 Armin Burgmeier Added application state frags controlling header sensitivity 2006-03-06 Armin Burgmeier Do not allow subscriptions to non-UTF-8-encoded documents 2006-03-06 Armin Burgmeier Store original encoding in document settings [fixes #65] 2006-03-05 Armin Burgmeier Added encoding selection in file chooser 2006-03-05 Armin Burgmeier Do not delete buffer after session closure, allow saving of closed sessions 2006-03-02 Philipp Kern Transform the ok button into a host one in the host dialog 2006-03-02 Philipp Kern Use a connect button instead of ok in the join dialog 2006-02-28 Armin Burgmeier Introduced document settings to store document's original encoding 2006-02-28 Armin Burgmeier Send UTF-8 as encoding 2006-02-26 Armin Burgmeier Disable subscribe button when subscription request has been sent [fixes #126] 2006-02-24 Armin Burgmeier alt+n switches tabs 2006-02-23 Armin Burgmeier Restored left gravity cursor on remote insert 2006-02-23 Armin Burgmeier Gobby seems to work now, but still needs further testing 2006-02-22 Armin Burgmeier Made Gobby compile again - does still not work 2006-02-19 Armin Burgmeier Adjusted GSelector to new selector principle 2006-02-14 Philipp Kern Experimental patch for client-initiated encryption 2006-02-06 Armin Burgmeier Replaced buffer wrappers by GSelector 2006-02-03 Armin Burgmeier Added obby/document.hpp include since it is heavily decoupled from obby itself 2006-02-02 Armin Burgmeier Changed buffer wrapper to match obby's new_net() 2006-01-30 Armin Burgmeier Temporary adjustments for document template parameter in document info 2006-01-29 Armin Burgmeier Temporary adjustments to document type template in obby::document_info 2006-01-26 Armin Burgmeier Changes for new net6 IO conditions 2006-01-11 Trac TODO updated 2006-01-07 Trac TODO updated 2005-12-31 Trac TODO updated 2005-12-13 Trac TODO updated 2005-12-11 Trac TODO updated 2005-12-10 Philipp Kern Forgot to translate at least one string 2005-12-08 Philipp Kern Translation roundup 2005-12-07 Philipp Kern Armin spotted the problem with encodings in the chat window 2005-11-24 Philipp Kern Translation roundup 2005-11-24 Philipp Kern Require net6-1.2 >= 1.2.1 2005-11-23 Trac TODO updated 2005-11-22 Armin Burgmeier Removed covariant returns from buffer wrapper 2005-11-21 Armin Burgmeier Do not crash if empty config is found 2005-11-20 Philipp Kern Search for obby in the paths specified by pkg-config 2005-11-20 Armin Burgmeier Give initial focus to close button in close confirmation dialog 2005-11-19 Armin Burgmeier Updated README and AUTHORS 2005-11-19 Armin Burgmeier Atomic write failed if the destination directory was not on the same device as the temporary directory, use normal write 2005-11-19 Trac TODO updated 2005-11-18 Armin Burgmeier Added icon.cpp to POTFILES.in 2005-11-18 Armin Burgmeier net6::gettext_package usage 2005-11-17 Armin Burgmeier documentlist.cpp was missing in POTFILES.in 2005-11-17 Armin Burgmeier Need to link against this for OLE Drag+Drop to work 2005-11-17 Armin Burgmeier Fixed a bug in Win32 Drag+Drop 2005-11-17 Armin Burgmeier Added find- and goto dialog to POTFILES.in 2005-11-17 Armin Burgmeier Goto line Dialog [fixes #79] 2005-11-17 Philipp Kern Protect the unpublish call 2005-11-17 Armin Burgmeier Adjusted Automake.am 2005-11-17 Armin Burgmeier Introduces ToggleWindow 2005-11-17 Armin Burgmeier Win32 Drag+Drop support [fixes #71] 2005-11-17 Trac TODO updated 2005-11-16 Armin Burgmeier Use WINDOW_TYPE_HINT_UTILITY for FindDialog as well 2005-11-16 Armin Burgmeier Set WINDOW_TYPE_HINT_UTILITY window_hint for tool windows 2005-11-16 Armin Burgmeier Fixed last dependency on 2005-11-16 Armin Burgmeier Format time string according to current locale 2005-11-15 Armin Burgmeier Win32 build fixes 2005-11-15 Philipp Kern Updated NEWS 2005-11-15 Philipp Kern Translation roundup 2005-11-15 Armin Burgmeier Win32 fixes in ProgressDialog's threading concept 2005-11-15 Armin Burgmeier Prompt when the user wants to exit Gobby and a session is still open [fixes #93] 2005-11-15 Armin Burgmeier Some UI fixes 2005-11-15 Armin Burgmeier Removed dependency on GNU regex library 2005-11-15 Armin Burgmeier Working FindDialog, removed regex support 2005-11-14 Philipp Kern String correction 2005-11-13 Armin Burgmeier Find dialog works better, but some regex stuff still does not work 2005-11-13 Philipp Kern Bugfix, transmit the value correctly 2005-11-13 Philipp Kern Remember the window positions 2005-11-13 Armin Burgmeier Non-working FindDialog 2005-11-13 Philipp Kern Moved the config from window to main 2005-11-13 Philipp Kern Expand online users by default 2005-11-13 Armin Burgmeier Added missing documentlist files 2005-11-13 Trac TODO updated 2005-11-13 Armin Burgmeier Added document list, only subscribed document are shown in folder 2005-11-12 Philipp Kern Added userlist image 2005-11-12 Armin Burgmeier Precreate Gobby icon 2005-11-12 Armin Burgmeier Removed empty paned from window 2005-11-12 Armin Burgmeier Added user list as tool window 2005-11-11 Armin Burgmeier Changed user list icons 2005-11-11 Armin Burgmeier Fixed a synchronisation loss when resubscribing to a document 2005-11-11 Armin Burgmeier Implemented tree-based userlist 2005-11-09 Trac TODO updated 2005-11-07 Armin Burgmeier Made folder scrollable, decreases minimum width if many tabs are open 2005-11-07 Armin Burgmeier Initialise colour selection dialog with button's current colour in Gobby::ColorButton 2005-11-07 Armin Burgmeier Userlist using TreeView; currently disfunctional! 2005-11-07 Trac TODO updated 2005-11-06 Armin Burgmeier Update title bar correctly upon reconnection [fixes #92] 2005-11-06 Armin Burgmeier Atomic write for documents, do not append newline on write [fixes #91] 2005-11-06 Armin Burgmeier Use a left-gravity cursor on remote insert operations [fixes #84] 2005-11-06 Philipp Kern Added translator credits and link to Rosetta 2005-11-05 Armin Burgmeier pre-select the save button in close confirmation dialog 2005-11-05 Armin Burgmeier Close document just unsubscribes from the document [fixes #83] 2005-11-05 Armin Burgmeier Fixed a German translation error 2005-11-05 Armin Burgmeier Show a close confirmation dialog according to GNOME HIG [fixes #105] 2005-11-05 Armin Burgmeier Convert resulting filename to utf8 in FileEntry 2005-11-05 Armin Burgmeier Added overwrite confirmation if gtkmm is >= 2.8 2005-11-05 Trac TODO updated 2005-11-04 Armin Burgmeier Set Gobby::Icon::gobby as default window icon 2005-11-04 Armin Burgmeier Fixed a memleak in the GtkSourceView wrapper 2005-11-04 Armin Burgmeier Adjustments for inc_flags/exc_flags 2005-11-04 Philipp Kern Allow to join servers with port numbers < 1024 2005-11-03 Armin Burgmeier Fixed another bug in configure.in 2005-11-03 Armin Burgmeier Fixed GTKMM-2.8 check 2005-11-03 Armin Burgmeier Allow to change name/colour directly if it is already in use [Scott Baker] 2005-11-03 Philipp Kern Added a check for Gtkmm's version (untested) 2005-11-03 Armin Burgmeier Do not use IPv6 when hosting a session 2005-11-03 Armin Burgmeier Patched Gobby for new obby::colour class 2005-11-03 Armin Burgmeier LogView::log takes optionally timestamp 2005-11-03 Philipp Kern Changed one translated string 2005-11-03 Philipp Kern Added Swedish translation, thanks to Daniel Nylander 2005-11-02 Armin Burgmeier Patched Gobby for new obby chat class 2005-11-02 Armin Burgmeier Updated translation po/pot files 2005-10-28 Armin Burgmeier Patched set_selection to take Gtk::TextIter and scroll to selected position 2005-10-28 Benjamin Herr Added GNU regex wrappers (not used yet because they suck at unicode) 2005-10-27 Armin Burgmeier Added Gobby::Document::set_selection 2005-10-29 Trac TODO updated 2005-10-21 Armin Burgmeier Temporary menu entry for saving a session [fixes #20] 2005-10-15 Trac TODO updated 2005-10-09 Armin Burgmeier Session to restore may be given in HostDialog 2005-10-09 Armin Burgmeier Introduces FileEntry 2005-10-09 Armin Burgmeier Removed trailing semicolon after namespace 2005-10-09 Trac TODO updated 2005-10-08 Trac TODO updated 2005-10-08 Trac TODO updated 2005-10-07 Philipp Kern Wrap the lines in the chat window [fixes #99] 2005-10-07 Philipp Kern Escape special characters in document title [fixes #101] 2005-10-07 Philipp Kern Translation roundup 2005-10-07 Philipp Kern Show current connection status in statusbar 2005-10-07 Trac TODO updated 2005-10-05 Philipp Kern Fixed thread creation issues 2005-10-05 Philipp Kern Compile fix by const_casting for g_atomic_int_get 2005-10-05 Trac TODO updated 2005-10-04 Armin Burgmeier Do not block when connecting to a remote host 2005-10-04 Armin Burgmeier Added Atomic class for Atomic boolean operations 2005-10-04 Armin Burgmeier Adjusted buffer wrappers to buffer's reusability 2005-10-01 Philipp Kern Do not show disconnected users as joined in chat 2005-10-01 Philipp Kern Make connected flag in userlist dependend on the user's value 2005-09-29 Trac TODO updated 2005-09-22 Michael Walter Added #include to to avoid being included later. 2005-09-22 Michael Walter Compile fix (m_client -> m_net.reset). 2005-09-22 Michael Walter Added missing return statement. 2005-09-22 Michael Walter Unicode fixes (was calling generic version with ASCII/ANSI strings). 2005-09-20 Armin Burgmeier Fixed usages of user_table's iterator for non-templated user_table 2005-09-19 Trac TODO updated 2005-09-17 Armin Burgmeier Fixed a crash on client-side connection loss 2005-09-15 Philipp Kern Removed pending changes and revision from the status bar 2005-09-15 Philipp Kern Opened new 0.3.x tree 2005-09-15 Philipp Kern Depend on net6-1.2 and obby-0.3 2005-09-15 Armin Burgmeier Avoid useless thread in JoinProgressDialog 2005-09-15 Armin Burgmeier Two additional checks concerning subscription 2005-09-15 Philipp Kern Added 0.3.0 NEWS item 2005-09-05 Philipp Kern Added a NEWS item to reflect the line encoding patch 2005-09-02 Philipp Kern Updated NEWS to reflect more changes 2005-09-14 Armin Burgmeier Adjustments to jupiter-using obby 2005-09-13 Trac TODO updated 2005-09-07 Armin Burgmeier Some more const-correctness 2005-09-06 Armin Burgmeier Adjusted buffer wrapper because peer has been renamed to user in net6 2005-07-28 Armin Burgmeier Fixed SEGV on win32 when creating/joining a session 2005-09-05 Philipp Kern Removed obsolete TODO items 2005-09-05 Trac TODO updated 2005-09-04 Philipp Kern Convert all different line encodings to UNIX ones 2005-09-02 Philipp Kern Added 0.2.1 and 0.2.2 to version history 2005-09-01 Armin Burgmeier Disable Glib's implicit charset conversion to the current locale 2005-08-31 Philipp Kern Fixed most unknown file extensions, added some more 2005-08-21 Philipp Kern Made the JoinDialog vbox non-conditional 2005-08-27 Trac TODO updated 2005-08-25 Armin Burgmeier net6::client::conn is now auto_ptr TODO updated 2005-08-23 Trac TODO updated 2005-08-18 Philipp Kern Updated AUTHORS to list contributors and translators 2005-08-18 Philipp Kern Added Spanish translation, thanks to Mario Palomo 2005-08-15 Trac TODO updated 2005-08-13 Trac TODO updated 2005-08-07 Trac TODO updated 2005-08-05 Philipp Kern Allow to open documents as unowned text, thanks to Ben Levitt [fixes #52] 2005-08-05 Trac TODO updated 2005-08-03 Trac TODO updated 2005-08-02 Philipp Kern Non-empty documents are flagged modified [ref #72] 2005-07-31 Trac TODO updated 2005-07-29 Philipp Kern Updated ChangeLog for release 2005-07-29 Trac TODO updated 2005-07-28 Philipp Kern Updated German translation 2005-07-28 Philipp Kern Updates from `make update-po' 2005-07-28 Armin Burgmeier Win32 build fix 2005-07-27 Armin Burgmeier Some fixes for buffer_wrappers 2005-07-27 Armin Burgmeier Fixed POTFILES.in for new io/ subdir 2005-07-27 Armin Burgmeier Emit content_changed signal in insert/erase_after [fixes #64] 2005-07-27 Armin Burgmeier Queried the cached subscribe value instead of requesting it from obby::document_info [fixes #63] 2005-07-27 Armin Burgmeier Moved buffer_wrapper to io/ subdir, enhanced portabaility 2005-07-27 Philipp Kern The last missing French string 2005-07-27 Philipp Kern Updated French translation by Peer Janssen 2005-07-27 Trac TODO updated 2005-07-26 Philipp Kern Better Win32 network handling announced 2005-07-26 Philipp Kern Added a readme and an installation guide [fixes #51] 2005-07-26 Philipp Kern Initialise the colour selection dialog with the current user colour 2005-07-26 Armin Burgmeier Column returns the real column of the text not just the nth character 2005-07-26 Armin Burgmeier User highlighting when name is mentioned in chat [fixes #6] 2005-07-26 Armin Burgmeier Prompt before closing modified documents 2005-07-26 Armin Burgmeier Save host/port settings into new config keys 2005-07-26 Armin Burgmeier Switch to tabs newly opened [fixes #27] 2005-07-26 Armin Burgmeier Connected/Subscribed flags in user list (fixes #12) 2005-07-25 Armin Burgmeier Check for valid UTF-8 after converting 2005-07-25 Armin Burgmeier Try to convert files with other encodings to UTF-8 [fixes #43] 2005-07-25 Philipp Kern Colourised tab labels [fixes #59] 2005-07-25 Philipp Kern Merged host and join dialog data just again, with separated ports and hostnames 2005-07-25 Philipp Kern Added more dots to the menu items 2005-07-25 Armin Burgmeier Format string adjustments 2005-07-25 Trac TODO updated 2005-07-24 Philipp Kern Depend on obby's 0.2.x API 2005-07-24 Philipp Kern Improved the Zeroconf handling 2005-07-24 Armin Burgmeier Added Window::get_current_document 2005-07-24 Armin Burgmeier Do not allow empty document names 2005-07-24 Armin Burgmeier Removed useless preferences_changed delegate 2005-07-24 Armin Burgmeier Update cursor position display in statusbar by remote changes 2005-07-24 Armin Burgmeier Modified flag [fixes #25] 2005-07-24 Philipp Kern Translation roundup 2005-07-24 Armin Burgmeier Added PasswordDialog 2005-07-24 Armin Burgmeier Corrections to file handling 2005-07-24 Armin Burgmeier Added toolbar settings into preferences [fixes #32] 2005-07-23 Philipp Kern Added some release notes directly to the sources 2005-07-23 Philipp Kern Adjusted the Zeroconf usage 2005-07-23 Armin Burgmeier First attempt to allow opening files by dropping them on a document, doesn't work. 2005-07-23 Armin Burgmeier Drag+Drop of files into open Gobby session 2005-07-23 Philipp Kern Set colour implemented 2005-07-21 Armin Burgmeier UNDO: Use 'session' config key for both client and host 2005-07-23 Armin Burgmeier No fixed size for the send button 2005-07-23 Armin Burgmeier Removed debug log 2005-07-23 Armin Burgmeier Colour presets [fixes #36] Thanks tom! :D 2005-07-23 Philipp Kern Use a tooltip instead of an expanded label 2005-07-23 Armin Burgmeier Smart home/end preference 2005-07-23 Armin Burgmeier Re-arranged status bar 2005-07-22 Armin Burgmeier Some more preferences 2005-07-23 Trac TODO updated 2005-07-22 Armin Burgmeier typo in preferences dialog fixed 2005-07-22 Armin Burgmeier Local preferences, syntax submenu 2005-07-22 Armin Burgmeier Fixed some messup 2005-07-22 Philipp Kern Fixed just another conflict 2005-07-22 Philipp Kern A cleanup patch to fix hundreds of conflicts 2005-07-22 Philipp Kern Changed the GtkSourceView dependency to a hard one 2005-07-22 Armin Burgmeier Fixed conflict caused by phil's security page 2005-07-22 Armin Burgmeier Introduced working preferences, some are missing 2005-07-22 Philipp Kern Added a Security stub to the preferences dialog sources 2005-07-21 Philipp Kern Changes to the About dialog 2005-07-21 Armin Burgmeier Small UI fixes 2005-07-21 Armin Burgmeier First preferences dialog approach 2005-07-21 Philipp Kern Used ngettext one time, updated templates a bit 2005-07-21 Armin Burgmeier Changes by libtool 2005-07-21 Armin Burgmeier Use 'session' config key for both client and host 2005-07-21 Philipp Kern Use the right directory to look for pixmaps 2005-07-21 Armin Burgmeier Small adjustments to net6 API change 2005-07-21 Trac TODO updated 2005-07-20 Armin Burgmeier Connect synchoniously to the server, would cause too much trouble otherwise 2005-07-20 Armin Burgmeier Gtk::manage dynamically allocated widgets 2005-07-20 Armin Burgmeier Fixed SEGV on connection error 2005-07-20 Armin Burgmeier Initialize Threading system in main() 2005-07-20 Armin Burgmeier Close icons in tab labels [ref #32] 2005-07-20 Armin Burgmeier Display current document in title bar 2005-07-19 Armin Burgmeier Progress dialogs 2005-07-18 Philipp Kern Added dependency on gthread-2.0, versioned one on net6 2005-07-17 Armin Burgmeier User authentication stuff 2005-07-15 Armin Burgmeier Global password 2005-07-15 Trac TODO updated 2005-07-03 Trac TODO updated 2005-07-01 Trac TODO updated 2005-06-30 Philipp Kern Highlight chat messages with the own nickname [fixes #6] 2005-06-30 Philipp Kern Pass the current session buffer to all client widgets 2005-06-25 Trac TODO updated 2005-06-24 Philipp Kern Removed two unnecessary includes from src/buffer_wrapper.cpp 2005-06-24 Philipp Kern French translation reviewed by Mohammed Adnene Trojette 2005-06-24 Philipp Kern Updated translation files 2005-06-24 Philipp Kern Updated German translation, still not fully completed 2005-06-23 Philipp Kern Catch-all update of the gettext stuff 2005-06-23 Philipp Kern Added French translation of Peer Janssen [fixes #50] 2005-06-23 Armin Burgmeier Fixes for changed obby record API 2005-06-17 Trac TODO updated 2005-06-16 Philipp Kern Removed autogen.sh, it is superseded by autoreconf 2005-06-16 Philipp Kern Return a const char pointer from gettext 2005-06-15 Philipp Kern Added word wrapping 2005-06-15 Armin Burgmeier Replace all non-ascii characters in UI XML [fixes #44] 2005-06-14 Philipp Kern One new string to translate, updated debconf template 2005-06-14 Philipp Kern Added missing conditionals related to GtkSourceView 2005-06-14 Philipp Kern Gobby depends on obby 0.2.0 or higher 2005-06-14 Armin Burgmeier Using obby::format_string instead of std::stringstream 2005-06-14 Armin Burgmeier Enable highlighting when subscribed 2005-06-14 Trac TODO updated 2005-06-13 Armin Burgmeier Adjustments for synchronisation on request. The core feature seems to work. The user interface, however, may be discussed and changed later on. This is just a first try to test the feature and to make Gobby compile with it. 2005-06-13 Trac TODO updated 2005-06-12 Trac TODO updated 2005-06-10 Philipp Kern Preliminary, but working Zeroconf support [ref #17] 2005-06-09 Philipp Kern Revised the German translation 2005-06-09 Philipp Kern Corrected AE color to BE colour 2005-06-09 Trac TODO updated 2005-06-08 Trac TODO updated 2005-06-07 Armin Burgmeier Adjusted code for changes user flags in libobby 2005-06-07 Philipp Kern Modified history by adding the missing 0.1.1 NEWS entry 2005-06-07 Philipp Kern Added dependency on Automake 1.9; added missing NEWS check 2005-06-07 Trac TODO updated 2005-06-06 Philipp Kern Ship gobby.desktop in the distribution 2005-06-06 Armin Burgmeier Adjusted for net6's error code transmission (#16) 2005-06-06 Armin Burgmeier Better win32 network implementation [fixes #2] 2005-06-05 Trac TODO updated 2005-06-04 Philipp Kern Updated ChangeLog for release 2005-06-04 Armin Burgmeier Fixed a segfault which occured sometimes when closing documents 2005-06-04 Armin Burgmeier Introduced DefaultDialog to allow to press Enter in dialogs 2005-06-04 Armin Burgmeier Show up user-colourising text immediately [fixes #22] 2005-06-04 Philipp Kern Removed the useless ENABLE_NLS check as we hard-depend on gettext 2005-06-04 Philipp Kern Translated missing strings into German 2005-06-04 Armin Burgmeier Use another default color as black [fixes #34] 2005-06-04 Armin Burgmeier Remove entities in on_folder_tab_switched 2005-06-04 Armin Burgmeier A much better fix for #24 2005-06-04 Philipp Kern Updated the gettext template and the German translation 2005-06-04 Armin Burgmeier Removed Umlauts from Language file names to build valid XML, renamed Document to View in menu 2005-06-04 Armin Burgmeier Update user colour in key press event handler if possible [fixes #24] If someone copied a part of the buffer, the tags applied to it were copied, too. If he pasted it then, the insert-text signal handler could not remove those tags because they were not yet applied. So it ended up in having to user colour tags applied to the new range of text: The one from the user who pasted the text and the one from the user who wrote the original text. This is solved by adding tags followed by a key press event not anymore in the insert-text event handler but in the key press event handler, which is called later, when the pasted tag already has been applied. Then, it may be removed and the one and only user colour tag can be set. 2005-06-03 Philipp Kern Use "n unsynced change(s)" instead of "in sync" 2005-06-03 Benjamin Herr Fixed my messup fixing the chat multiline handling 2005-06-03 Trac TODO updated 2005-06-02 Trac TODO updated 2005-06-02 Trac TODO updated 2005-06-02 Trac TODO updated 2005-06-02 Trac TODO updated 2005-06-02 Philipp Kern Merging the multi-line chat fixes 2005-05-31 Benjamin Herr Splitting up received lines of chat by newlines, added todo items 2005-06-02 Armin Burgmeier Menu to switch the syntax colouring scheme [fixes #4] 2005-06-01 Armin Burgmeier Option to disable line numbers [fixes #7] 2005-06-01 Armin Burgmeier Show revision and sync state in statusbar [fixes #13] 2005-06-01 Armin Burgmeier Fixed some errors caused by recent libobby API changes 2005-05-30 Armin Burgmeier Set correct notebook tab after startup 2005-05-29 Armin Burgmeier Use of local_buffer instead of dynamic casts [ref #9] 2005-05-29 Armin Burgmeier Initialise size of paned widgets [fixes #5] 2005-05-29 Armin Burgmeier Allow the user to save documents after connection loss [fixes #1] 2005-05-29 Armin Burgmeier Disable close and save buttons if no more documents are open 2005-05-28 Armin Burgmeier Send lines separately in chat 2005-05-26 Philipp Kern Changed the desktop item's name to be a bit more compilant 2005-05-26 Armin Burgmeier Segfault fixed some time ago... 2005-05-22 Philipp Kern Ebuild change: Depend on gtksourceview, use GNOME integration 2005-05-21 Philipp Kern Load icon from file when compiled --with-gnome 2005-05-21 Philipp Kern Resized the 128x128 XPM to 32x32 2005-05-20 Philipp Kern Added missing assert include 2005-05-20 Philipp Kern Added build-dependency on net6-1.0 2005-05-19 Philipp Kern configure now detects the presence of GtkSourceView automatically 2005-05-19 Philipp Kern Makefile.am had a wrong conditional statement 2005-05-15 Philipp Kern Serious bug exploited, po/ permission-related build fix 2005-05-15 Philipp Kern Added GNOME integration 2005-05-11 Philipp Kern Added gzip'ed XPM version of the Gobby logo 2005-05-10 Armin Burgmeier Fixed a segfault which occured sometimes on the exit 2005-05-08 Armin Burgmeier Shuffled some files around, fixed non-gtksourceview build [phil] 2005-05-08 Armin Burgmeier WIN32 build fix (resource files build correctly) [phil] 2005-05-08 Armin Burgmeier gobby resource file until autotools are able to build it 2005-05-08 Armin Burgmeier Sized gobby.ico to 48x48 Bigger icons just waste memory on windows, they will never be shown in their full size. 2005-05-08 Philipp Kern Try to build gobby.res from top-level Makefile.am 2005-05-08 Philipp Kern Use Automake for win32/ subdirectory 2005-05-08 Philipp Kern Fixed make dist 2005-05-08 Philipp Kern Delocalised two error strings 2005-05-08 Philipp Kern po reflect new line numbers within the source files 2005-05-08 Philipp Kern Include contrib/ and win32/ in make dist 2005-05-08 Philipp Kern Added more WIN32 build stuff 2005-05-07 Philipp Kern Suppress the console window in favour of a real GUI application 2005-05-07 Armin Burgmeier Win32 does not have localtime_r We do not need thread safety anyway... 2005-05-07 Armin Burgmeier make dist requires ChangeLog... 2005-05-07 Armin Burgmeier win32 resource file for the gobby icon 2005-05-07 Armin Burgmeier User colourising 2005-05-07 Armin Burgmeier Added keyboard shortcuts 2005-05-07 Armin Burgmeier Moved language manager to folder 2005-05-07 Armin Burgmeier Replaced _ macro by an inline function 2005-05-07 Armin Burgmeier Separate mime map 2005-05-06 Armin Burgmeier StatusBar showing cursor position and current language 2005-05-06 Philipp Kern Added more TODO items 2005-05-05 Philipp Kern Made the TODO list more readable 2005-05-05 Philipp Kern A missing shell call produced build errors 2005-05-05 Philipp Kern Set scripts executable and use a partial checkout in the ebuild 2005-05-05 Philipp Kern Added missing po/Makefile.in.in 2005-05-05 Philipp Kern Gobby i18n, German translation added 2005-05-05 Philipp Kern Ignore more intermediate files 2005-05-05 Philipp Kern Added mkinstalldirs from Autotools 2005-05-05 Armin Burgmeier auto* updated some auto-generated files... 2005-05-05 Armin Burgmeier Gobby sends positions now in bytes and no longer in characters. libobby takes all positions in bytes to having not to care about different encodings. 2005-05-04 Philipp Kern Display a Gtk::MessageDialog on an unhandled exception 2005-04-27 Philipp Kern Fixed gobby to compile without gtksourceview 2005-04-26 Philipp Kern Insert parameter placeholder corrected 2005-04-25 Philipp Kern Ugly fix to get the times right 2005-04-25 Armin Burgmeier Implemented timestamps in logview 2005-04-25 Philipp Kern Some corrections to the file types 2005-04-24 Philipp Kern Set current name in the Save FileChooser 2005-04-24 Philipp Kern Added two new TODO items 2005-04-22 Armin Burgmeier Code highlighting by file extension 2005-04-22 Armin Burgmeier Some GtkSourceView wrapper fixes and extensions 2005-04-22 Armin Burgmeier GtkSourceLanguagesManager wrapper 2005-04-21 Armin Burgmeier Wrapped GtkSourceLanguage 2005-04-21 Armin Burgmeier Use a monospaced font for the TextView 2005-04-20 Armin Burgmeier GtkSourceBuffer wrapper 2005-04-18 Philipp Kern Save document was missing in the source 2005-04-17 Philipp Kern Check for howl in libobby 2005-04-17 Armin Burgmeier sourceview/-subdir for sourceview wrappers, Gtk::SourceView actually works 2005-04-17 Philipp Kern Gather host information correctly 2005-04-17 Armin Burgmeier Win32 buffer_wrappers using a timer 2005-04-15 Armin Burgmeier Added tom as artist 2005-04-15 Armin Burgmeier Added inline gobby icon 2005-04-15 Philipp Kern Added artwork credits 2005-04-15 Thomas Glatt Added Gobby artwork 2005-04-15 Armin Burgmeier Call correct base_function in Gobby::Host::on_connect 2005-04-15 Armin Burgmeier Adjusted buffer_wrapper for net6 API corrections 2005-04-15 Armin Burgmeier Added some first wrapper code for GtkSourceView -- disfunctional 2005-04-15 Philipp Kern Added framework to use SourceView instead of TextView 2005-04-14 Philipp Kern Added boringfile 2005-04-14 Philipp Kern Added save document hooks, but w/o real functionality 2005-04-14 Philipp Kern Use the new include path of libobby 2005-04-14 Philipp Kern Check for obby-1.0 instead of lobby-1.0 2005-04-14 Philipp Kern Implemented open document 2005-04-14 Philipp Kern Removed ui.xml from the ebuild 2005-04-14 Philipp Kern XML UI descriptions are now present within src/header.cpp 2005-04-14 Philipp Kern Compile the right buffer wrappers for their corresponding platform 2005-04-14 Philipp Kern Fix buffer wrapper to use generic on !WIN32 2005-04-12 Armin Burgmeier Splitted buffer_wrapper up to buffer_wrapper_generic and buffer_wrapper. buffer_wrapper_win32 will follow. 2005-04-11 Philipp Kern Implemented close a bit more correctly, updated TODO 2005-04-11 Philipp Kern Remove current document, *hopefully* w/o memory leak 2005-04-10 Thomas Glatt Ebuild for gobby 2005-04-10 Philipp Kern Resolved conflict in Makefile.am related to WIN32 build 2005-04-10 Philipp Kern Added WIN32 build support 2005-04-09 Armin Burgmeier Win32 build fixes; buffer_wrappers still use a timer on win32 2005-04-09 Armin Burgmeier Distribute header files with 'make dist' 2005-04-09 Armin Burgmeier Replaced ERROR with IOERROR in buffer_wrappers according to new net6 API 2005-04-09 Armin Burgmeier Fixed missing include in buffer_wrapper.cpp 2005-04-09 Armin Burgmeier Use the file name of the file to open as document title 2005-04-09 Philipp Kern Relicensed under the GPL Reasoning: There was never the intention to license gobby under the LGPL. COPYING included the GPL but all the license headers referred to the LGPL. This is now fixed in accordance with all the contributing authors. ACK: - Armin Burgmeier - Philipp Kern - Benjamin Herr 2005-04-09 Armin Burgmeier Watching on Glib::signal_io() instead of using a timer 2005-04-08 Armin Burgmeier Fixed a crash in gobby which occured if a connection has been lost 2005-04-08 Armin Burgmeier Changed Menu-Action 'Help' to 'MenuHelp', for consistency with other actions 2005-04-08 Armin Burgmeier Resolved a conflict in inc/window.hpp and src/window.cpp 2005-04-08 Armin Burgmeier Document creation 2005-04-08 Philipp Kern Added about dialog 2005-04-08 Benjamin Herr Added entrydialog to prompt for strings 2005-04-08 Armin Burgmeier Fixed gobby synchronization 2005-04-08 Benjamin Herr Added menu items for *Document, fixed typo 2005-04-08 Philipp Kern Moved config_.hpp to config.hpp, introduced features.hpp 2005-04-07 Philipp Kern Added initial NEWS content and Ben to AUTHORS 2005-04-07 Philipp Kern Added document toolbar button stubs 2005-04-07 Philipp Kern Receive document title from libobby 2005-04-07 Armin Burgmeier h4ck 2005-04-07 Benjamin Herr Todo: Exceptions? 2005-04-07 Benjamin Herr Fixed window to also connect chat signals on join 2005-04-07 Benjamin Herr Fixed indenting spaces to be tabs 2005-04-07 Armin Burgmeier Document synchronisation thorugh libobby 2005-04-07 Benjamin Herr Added handling of chat events from m_chat and m_buffer 2005-04-05 Armin Burgmeier Initial TODO file 2005-04-04 Philipp Kern Depend on gtkmm-2.4 >= 2.6.0 2005-04-03 Armin Burgmeier Document class representing an obby::document 2005-04-03 Armin Burgmeier Event delegations 2005-04-03 Armin Burgmeier XML config file in home dir; requires libxml++ 2005-04-02 Armin Burgmeier Added create & join dialogs 2005-04-02 Armin Burgmeier Skeleton of the UI 2005-04-02 Armin Burgmeier Header, GTK naming scheme 2005-03-30 Philipp Kern Autotoolised gobby 2005-03-30 Armin Burgmeier Empty gobby window Makefile.am000066400000000000000000000054011304552132100131060ustar00rootroot00000000000000SUBDIRS = m4 . icons po if HAVE_GNOME_DOC_UTILS SUBDIRS += help endif # Tell aclocal where to find `.m4' files. ACLOCAL_AMFLAGS = -I m4 dist_man_MANS = gobby-0.5.1 bin_PROGRAMS = gobby-0.5 noinst_HEADERS = gobby_0_5_SOURCES = BUILT_SOURCES = EXTRA_DIST = CLEANFILES = DISTCLEANFILES = MAINTAINERCLEANFILES = include code/Makefile.am include win32/Makefile.am gobby_0_5_LDADD = \ $(gobby_LIBS) \ $(infinote_LIBS) \ $(LIBS) if WIN32 gobby_0_5_LDADD += -lws2_32 gobby_0_5_LDFLAGS = \ -Wl,--enable-runtime-pseudo-reloc \ -mwindows \ win32/gobby.res endif # PRIVATE_ICONS_DIR is required by application.cpp to get access to the # private icons that are installed into prefix/share/gobby-0.5/icons instead # of prefix/share/icons. We also set PUBLIC_ICONS_DIR explicitely so that # we find the application icon if it is installed in a non-standard prefix. AM_CPPFLAGS = $(gobby_CFLAGS) $(infinote_CFLAGS) \ -I$(top_srcdir)/code \ -DPRIVATE_ICONS_DIR=\""$(datadir)/gobby-0.5/icons"\" \ -DPUBLIC_ICONS_DIR=\""$(datadir)/icons"\" # .desktop file if !WIN32 desktopdir = $(datadir)/applications desktop_in_files = gobby-0.5.desktop.in desktop_DATA = $(desktop_in_files:.desktop.in=.desktop) appdatadir = $(datadir)/appdata appdata_DATA = gobby-0.5.appdata.xml endif gsettings_SCHEMAS = de.0x539.gobby.gschema.xml EXTRA_DIST += de.0x539.gobby.gschema.xml.in CLEANFILES += de.0x539.gobby.gschema.xml DISTCLEANFILES += de.0x539.gobby.gschema.xml MAINTAINERCLEANFILES += $(gsettings_SCHEMAS:.xml=.valid) @GSETTINGS_RULES@ clean-local: -rm -rf gobby-0.5.desktop @INTLTOOL_DESKTOP_RULE@ EXTRA_DIST += README.md # Debian seems to need an XPM of the program icon, so we provide one here. # It's currently not used for anything else, though. EXTRA_DIST += gobby-0.5.xpm # For the manual EXTRA_DIST += gnome-doc-utils.make gobby-0.5.desktop.in gobby-0.5.appdata.xml DISTCLEANFILES += gnome-doc-utils.make DISTCHECK_CONFIGURE_FLAGS = --disable-scrollkeeper MAINTAINERCLEANFILES += \ ChangeLog distclean-local: if test "$(srcdir)" = "."; then :; else \ rm -f ChangeLog; \ fi ChangeLog: $(AM_V_GEN) if test -d "$(srcdir)/.git"; then \ (GIT_DIR=$(top_srcdir)/.git ./missing --run git log \ 6570cc49a6c2c29fe9cf4c85e3b74f133ae4c1f8..HEAD --stat=78) \ | fmt --split-only --width=78 > $@.tmp \ && cat $@.tmp ChangeLog.manual ChangeLog.pre-0.5 > $@ && $(RM) $@.tmp \ || ($(RM) $@.tmp; \ echo Failed to generate ChangeLog, your ChangeLog may be outdated >&2; \ (test -f $@ || echo git-log is required to generate this file >> $@)); \ else \ test -f $@ || \ (echo A git checkout and git-log is required to generate ChangeLog >&2 && \ echo A git checkout and git-log is required to generate this file >> $@); \ fi .PHONY: ChangeLog NEWS000066400000000000000000000174411304552132100115600ustar00rootroot00000000000000Gobby ===== Version 0.5.0: * Allow to run a server within the Gobby process; infinoted is no longer required to connect to other people * Added support for user accounts and different permissions for different accounts * Added limited support for chat nodes in the document tree * Added an option to set the opacity with which user colors are shown behind the text. * Added Fullscreen option and Ctrl++ and Ctrl+- shortcuts to increase/decrease the font size. * Gobby can open infinote://XXX URIs given on the command line to directly open one or more documents at startup. Version 0.4.94: * Show cursors and selections of remote users in the own viewport and scrollbar * Add a "Disconnect from Server" option to the context menu * When double-clicking a user in the user list jump to its current cursor position * Allow gobby to be built against GTK+ 3 * Fixed a bug which prevented "Save All" from working Version 0.4.93: * Added IRC-like chat * Added functionality to export a document to HTML (#245) * Sort browser entries * Allow selecting multiple files when opening files * Allow resetting user colors in a document (Bug #211) * Added a Connect to Server option to the File menu * Undo words instead of individual characters * When starting Gobby twice then send commands to an existing Gobby (if any) unless the -n command line option is given. * Added an option to highlight spaces and tabs Version 0.4.92: * Support running Gobby on Windows 2000. * Added Icons for HCLP and HCLPI icon themes (Bug #413, Patrick Wade). * Make sure terminating newline characters are saved correctly * Added /etc/ssl/certs/ca-certificates.crt as default trust file, if available. * Don't mess up saving documents whose first line is empty. * Write config files in $XDG_CONFIG_HOME instead of $HOME (Bug #437, Cristian Klein). * Display colored pencil icons in a document's tab for each user that made a change since the document has been viewed the last time (Bug #66, Mattis Manzel). Version 0.4.91: * Keep track of a document's modified state between Undo/Redo operations * Scroll to Cursor after Cut or Paste * Added HCLPI icon of the Gobby Logo (Thanks to Thomas Glatt) * Added an Autosave Option in the preferences * Remember previous host names in "Direct Connection" entry * Added an "Open Location" dialog to open arbitrary URIs * Added a tooltip when hovering text, showing the author of that text Version 0.4.90: * Use libinfinity instead of libobby as backend * Allow connection to multiple servers * Redesigned UI to better match normal text editors, and to be more HIG-compliant * Added Undo support (via libinfinity) * Require GtkSourceView 2.x * Some features of the previous versions, especially self-hosting, are not implemented in 0.4.90. This will be added in a later version. Version 0.4.6: * Require gtksourceview 1.8 to enable indent-on-tab * Require obby 0.4.5 to support IPv6 autodiscovery * Several utility windows close on escape * Compilation fixes for gtksourceview 2.0 * New (partial) translations: - Brazilian Portuguese - Polish - Korean Version 0.4.5: * Important bugfixes: - fixed the save routine for files not encoded in UTF-8 - allow Gobby to run inside paths containing non-ASCII characters Version 0.4.4: * Fixed two crashes: one on Windows Vista related to the chat widget, and one triggered on rejoins with multiple documents opened in the editor * GtkSourceView's C API is called directly * The document list is now sorted alphabetically * New translations: - Italian - Russian Version 0.4.3: * Find and Goto dialogs are now focused when invoked * Zeroconf failures are now only printed on the console, no dialog is invoked * Preference option added to subscribe automatically to all documents in a session * Doubleclicks on documents in the user list now opens them if they are not already opened * Window highlights itself only if the chat widget is open * Toolbar style "text besides icon" is now supported * Tab bar is only shown if more than one document is opened Version 0.4.2: * Gobby now opens absolute file names passed via command line * View is now kept when remote operations affect the current position, through appropriate scrolling * Documents could be subscribed by double-clicking on them in the list * Local files are not flagged as modified anymore * Urgency hint is now configurable * Windows compilation fixes Version 0.4.1: * Release with no further important changes. Version 0.4.0: * Session encryption through GNUTLS * Reduced CPU usage and memory footprint through the removal of one supplemental document copy * Usability enhancements: - New button to close the chat window - Font types and sizes could now be set for the text editor from within Gobby - Documents could now be switched using Ctrl-Alt-Page-Down and Ctrl-Alt-Page-Up - Subscribe button is deactivated after it has been hit once for a document - Documents with the same name are suffixed with a number - Dialogs to host and join sessions are respawned if the attempt failed - Application highlights itself on new chat messages - Documents could be opened in an existing Gobby session by passing their filename as a command-line argument to the application - Sessions could be joined by passing the host and port via command-line options - User-modified colour palettes are now saved in the configuration Version 0.3.0: * This release depends on net6 1.2.0 and obby 0.3.0, it is *not* backwards- compatible protocol-wise to the 0.2.x series. * No more pending changes due to the new merging algorithm * Cursor is left-gravity now, which relieves some problems with newlines * Joins are now threaded and thus abortable * Connection status is displayed in the status bar * Lines in the chat log are now wrapped to the display width * Session serialisation and restore * Name and colour could be changed directly when they are already in use * User and document list are now separate windows, their position and state is preserved by default * Close document now just unsubscribes from the document * Scrollable document folder * Search and replace * New translations: - Swedish (complete) - Catalan (partial) - Chinese (partial) - Spanish (almost complete) Version 0.2.2: * Gobby no longer crashes when a document is closed after quitting a session, spotted and fixed by Kanru Chen. * Some new file extensions are mapped to their corresponding MIME type * Files saved are no longer converted to the charset settings of the current locale * Line endings of loaded files are now unified to result only in UNIX ones * Added Spanish translation, thanks to Mario Palomo Version 0.2.1: * This bugfix release features a recompile of the Windows version against more hardware-independent libraries. Version 0.2.0: * This version is *not* backwards-compatible to 0.1.0 due to heavy changes within obby. It depends on obby 0.2.0 and net6 1.1.0. * New features (in no particular order): - Document subscriptions - Global session passwords - Basic user authentication to protect the nickname - Preferences pane - Zeroconf support - Colour presets - Colour changes from within the session - Auto indentation - Drag'n'drop of documents into the session - Create document works without a delay - Better Win32 network handling * Changes in the build system: - It is now required to provide `gtksourceview-1.0', the depedency is no more optional. - Gobby now links against `gthread-2.0' which is included in `glibmm-2.4'. Version 0.1.1: * Bugfix release: Include contrib/gobby.desktop to fix the non-working build when configured ``--with-gnome''. Version 0.1.0: * Initial release. README.md000066400000000000000000000100361304552132100123310ustar00rootroot00000000000000# Gobby: First Contact [![Build Status](https://travis-ci.org/gobby/gobby.svg?branch=master)](https://travis-ci.org/gobby/gobby) ## Synopsis Gobby is a free collaborative editor. This means that it provides you with the possibility to edit files simultaneously with other users over a network. The platforms on which you could use Gobby are so far Microsoft Windows, Linux, Mac OS X and other Unix-like ones. Developed with the GTK+ toolkit it integrates nicely into the GNOME desktop environment if you want it to. ## Features - Realtime collaboration through encrypted connections (including PFS) - Each user has its own colour to be identified by others - Local group Undo - Group chat - Shows cursors and selections of remote users - Sidebar with all the others having joined the session - Syntax highlighting, auto indentation, configurable tab width - Multiple documents in one session - Zeroconf support (optional) - Unicode support - Internationalisation - Highly configurable dedicated server - Sophisticated Access Control Lists (ACLs) - Cross-platform: Microsoft Windows, Linux, Mac OS X, other flavours of Unix - Free software, licenced under the terms of the ISC license ## Requirements - libinfinity (0.7.x) - Glib (>= 2.18.0) - Glibmm (>= 2.18.0) - libxml++ (>= 2.6.0) - libgsasl (>= 0.2.21) - Gtkmm-3.0 (>= 3.0) - GtkSourceView-3.0 (>= 3.0) ## More information The development of Gobby is coordinated on github at . The primary distribution point is . ### How to report bugs Bugs should be files as issues on the github issue tracker at . ### How to get involved We urgently seek all kind of people who could help us in any way. Firstly we need documentation writers who could help us crafting a help file and some standalone documentation for the web. Secondly, if you are skilled in C/C++ programming, we could need helping hands with programming. A starting point could be [github issues](https://github.com/gobby/gobby.git). And last, but certainly not least, we need translators who get their hands at Gobby's string templates to get it localised. Thus more could use Gobby in their native language. Just contact us, if in doubt, for more details. There are also two mailing lists which should provide a mean of contact to other Gobby users and to the developers. - *obby-announce*: A moderated list used to announce new releases of Gobby and its foundation, libinfinity. To subscribe, please send an email with the topic "subscribe" to . A list archive is available at . - *obby-users*: Discussions about Gobby's usage and installation problems. The announcements are also posted there. To subscribe, please send an email with the topic "subscribe" to . A list archive is available at . ## Licensing Copyright (C) 2008-2015, Armin Burgmeier Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ## Development To get in contact with the developers, please use the obby-users mailing list mentioned above, or open an issue on github. To get the up-to-date Gobby code to hack on, use `git clone https://github.com/gobby/gobby.git`. Pull requests against our [GitHub repository](https://github.com/gobby/gobby) are appreciated. TODO000066400000000000000000000014741304552132100115500ustar00rootroot00000000000000Modernization: Move non-window-specific functionality from Window to Application Possibility for regex search and async search Make one gobby process handle multiple windows, then don't make -n spawn another instance but another window Some features that would be nice for a final Gobby 1.0 release. We could perhaps do pre-releases with some of these missing before: - Add base preferences class with Preferences::Option definition to util/, inherit in core/, move ClosableFrame to util/ - Show in tab and userlist when others are typing, using an icon - Typing sounds (libcanberra?) These things need to be implemented in infinote, but again would be nice to have: - Show other user's viewport position in the scrollbar, instead or in addition to cursor position - Show what tab another user currently is in autogen.sh000077500000000000000000000002121304552132100130460ustar00rootroot00000000000000#!/bin/sh -e glib-gettextize --copy --force intltoolize --copy --force --automake gnome-doc-prepare autoreconf -f -i && automake -a -f code/000077500000000000000000000000001304552132100117645ustar00rootroot00000000000000code/.gitignore000066400000000000000000000001371304552132100137550ustar00rootroot00000000000000*.a features.hpp features.hpp.in features.hpp.in~ stamp-h1 gobby-resources.h gobby-resources.c code/Makefile.am000066400000000000000000000026751304552132100140320ustar00rootroot00000000000000include code/util/Makefile.am include code/core/Makefile.am include code/operations/Makefile.am include code/dialogs/Makefile.am include code/commands/Makefile.am gobby_0_5_SOURCES += \ code/application.cpp \ code/main.cpp \ code/window.cpp \ code/gobby-resources.c noinst_HEADERS += \ code/application.hpp \ code/window.hpp \ code/gobby-resources.h appicondir = $(datadir)/pixmaps pixmapdir = $(datadir)/pixmaps/gobby-0.5 resources = \ code/resources/ui/browser-context-menu.ui \ code/resources/ui/connection-dialog.ui \ code/resources/ui/connection-info-dialog.ui \ code/resources/ui/document-location-dialog.ui \ code/resources/ui/entry-dialog.ui \ code/resources/ui/find-dialog.ui \ code/resources/ui/goto-dialog.ui \ code/resources/ui/initial-dialog.ui \ code/resources/ui/menu.ui \ code/resources/ui/open-location-dialog.ui \ code/resources/ui/password-dialog.ui \ code/resources/ui/preferences-dialog.ui \ code/resources/ui/toolbar.ui code/gobby-resources.h: code/resources/gobby.gresources.xml glib-compile-resources $< \ --target=$@ --sourcedir=$(top_srcdir)/code/resources \ --c-name _gobby --generate-header code/gobby-resources.c: code/resources/gobby.gresources.xml $(resources) glib-compile-resources $< \ --target=$@ --sourcedir=$(top_srcdir)/code/resources \ --c-name _gobby --generate-source BUILT_SOURCES += code/gobby-resources.h code/gobby-resources.c CLEANFILES += code/gobby-resources.h code/gobby-resources.c code/application.cpp000066400000000000000000000167651304552132100150120ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "core/menumanager.hpp" #include "core/applicationactions.hpp" #include "application.hpp" #include "features.hpp" // Needed to register Gobby resource explicitly: extern "C" { #include "gobby-resources.h" } #include "commands/application-commands.hpp" #include "commands/help-commands.hpp" #include "util/i18n.hpp" #include "util/file.hpp" #include #include #include #include #include #include namespace { std::string gobby_localedir() { #ifdef G_OS_WIN32 gchar* root = g_win32_get_package_installation_directory_of_module( NULL); gchar* temp = g_build_filename(root, "share", "locale", NULL); g_free(root); gchar* result = g_win32_locale_filename_from_utf8(temp); g_free(temp); std::string cpp_result(result); g_free(result); return cpp_result; #else return GOBBY_LOCALEDIR; #endif } } class Gobby::Application::Data { public: Data(Gobby::Application& application); // TODO: Does the config object really need to stay around, or can // it be thrown away after we have loaded the preferences? Config config; FileChooser file_chooser; Preferences preferences; CertificateManager certificate_manager; GtkSourceLanguageManager* language_manager; ApplicationActions application_actions; MenuManager menu_manager; ApplicationCommands m_application_commands; HelpCommands m_help_commands; }; Gobby::Application::Data::Data(Gobby::Application& application): config(config_filename("config.xml")), preferences(config), certificate_manager(preferences), language_manager(gtk_source_language_manager_get_default()), application_actions(application), menu_manager(language_manager), m_application_commands(application, application_actions, file_chooser, preferences, certificate_manager), m_help_commands(application, application_actions) { // Remove configuration file, since we have transferred the options // now to GSettings. g_unlink(config_filename("config.xml").c_str()); } Gobby::Application::Application(): Gtk::Application("de._0x539.gobby", Gio::APPLICATION_HANDLES_OPEN) { setlocale(LC_ALL, ""); bindtextdomain(GETTEXT_PACKAGE, gobby_localedir().c_str()); bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); // There is no on_handle_local_options default handler in // Gio::Application: signal_handle_local_options().connect( sigc::mem_fun(*this, &Application::on_handle_local_options), false); // TODO: Here, probably N_(...) should be used for the translatable // strings, so that the option entry array can be static. However, // in this case the gettext translation domain cannot be set. // c.f. bugzilla.gnome.org #736637. // TODO: The only purpose of the G_OPTION_REMAINING option is to show // the arg_description in the --help output. However, in that case // the "open" signal is not emitted, since Glib assumes this is just // another option that we are handling ourselves. We could work around // it by handling the "command-line" signal instead of the "open" // signal. const GOptionEntry entries[] = { { "version", 'v', 0, G_OPTION_ARG_NONE, NULL, _("Display version information and exit"), NULL }, { "new-instance", 'n', 0, G_OPTION_ARG_NONE, NULL, _("Start a new gobby instance also if there is one " "already running"), NULL /*}, { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, NULL, NULL, N_("[FILE1 or URI1] [FILE2 or URI2] [...]") */}, { NULL } }; g_application_add_main_option_entries(G_APPLICATION(gobj()), entries); } Glib::RefPtr Gobby::Application::create() { return Glib::RefPtr( new Application); } int Gobby::Application::on_handle_local_options( const Glib::RefPtr& options_dict) { bool display_version; if(options_dict->lookup_value("version", display_version)) { std::cout << "Gobby " << PACKAGE_VERSION << std::endl; return 0; } bool new_instance; if(options_dict->lookup_value("new-instance", new_instance)) { set_flags(get_flags() | Gio::APPLICATION_NON_UNIQUE); options_dict->remove("new-instance"); } // Continue normal processing return -1; } void Gobby::Application::on_startup() { Gtk::Application::on_startup(); // Register Gobby resource explicitly -- GCC constructors do not // work for executables? _gobby_get_resource(); try { Gtk::Window::set_default_icon_name("gobby-0.5"); GError* error = NULL; if(inf_init(&error) != TRUE) throw Glib::Error(error); Gtk::IconTheme::get_default()->append_search_path( PUBLIC_ICONS_DIR); Gtk::IconTheme::get_default()->append_search_path( PRIVATE_ICONS_DIR); // Allocate the per-application data. This cannot be // done earlier, such as in the contrutor, since we only // need to do it if we are the primary instance. m_data.reset(new Data(*this)); set_app_menu(m_data->menu_manager.get_app_menu()); set_menubar(m_data->menu_manager.get_menu()); m_gobby_window = new Gobby::Window( m_data->config, m_data->language_manager, m_data->file_chooser, m_data->preferences, m_data->certificate_manager); m_window.reset(m_gobby_window); add_window(*m_gobby_window); m_window->show(); } catch(const Glib::Exception& ex) { handle_error(ex.what()); } catch(const std::exception& ex) { handle_error(ex.what()); } } void Gobby::Application::on_activate() { Gtk::Application::on_activate(); if(m_window.get()) m_window->present(); } void Gobby::Application::on_open(const type_vec_files& files, const Glib::ustring& hint) { Gtk::Application::on_open(files, hint); // If we don't have a window we ignore the file open request. This can // for example happen when Gobby was launched with command line // arguments, but the Gobby initialization failed. if(!m_gobby_window) return; Operations::file_list non_infinote_files; for(type_vec_files::const_iterator iter = files.begin(); iter != files.end(); ++iter) { Glib::RefPtr file = *iter; if(file->get_uri_scheme() == "infinote") m_gobby_window->subscribe(file->get_uri()); else non_infinote_files.push_back(file); } if(!files.empty()) m_gobby_window->open_files(non_infinote_files); } void Gobby::Application::handle_error(const std::string& message) { std::unique_ptr dialog( new Gtk::MessageDialog( "Failed to startup Gobby", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, false)); dialog->signal_response().connect( sigc::hide(sigc::mem_fun(*dialog, &Gtk::Window::hide))); dialog->set_title("Gobby"); dialog->set_secondary_text(message); m_window = std::move(dialog); add_window(*m_window); m_window->show(); } code/application.hpp000066400000000000000000000030321304552132100147760ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_APPLICATION_HPP_ #define _GOBBY_APPLICATION_HPP_ #include "window.hpp" #include namespace Gobby { class Application: public Gtk::Application { public: static Glib::RefPtr create(); protected: int on_handle_local_options( const Glib::RefPtr& options_dict); virtual void on_startup(); virtual void on_activate(); virtual void on_open(const type_vec_files& files, const Glib::ustring& hint); void handle_error(const std::string& message); class Data; std::unique_ptr m_data; Application(); std::unique_ptr m_window; Gobby::Window* m_gobby_window; }; } #endif // _GOBBY_APPLICATION_HPP_ code/commands/000077500000000000000000000000001304552132100135655ustar00rootroot00000000000000code/commands/.gitignore000066400000000000000000000000241304552132100155510ustar00rootroot00000000000000libgobby-commands.a code/commands/Makefile.am000066400000000000000000000021121304552132100156150ustar00rootroot00000000000000include code/commands/file-tasks/Makefile.am gobby_0_5_SOURCES += \ code/commands/application-commands.cpp \ code/commands/auth-commands.cpp \ code/commands/autosave-commands.cpp \ code/commands/browser-commands.cpp \ code/commands/browser-context-commands.cpp \ code/commands/edit-commands.cpp \ code/commands/file-commands.cpp \ code/commands/folder-commands.cpp \ code/commands/help-commands.cpp \ code/commands/subscription-commands.cpp \ code/commands/synchronization-commands.cpp \ code/commands/user-join-commands.cpp \ code/commands/view-commands.cpp noinst_HEADERS += \ code/commands/application-commands.hpp \ code/commands/auth-commands.hpp \ code/commands/autosave-commands.hpp \ code/commands/browser-commands.hpp \ code/commands/browser-context-commands.hpp \ code/commands/edit-commands.hpp \ code/commands/file-commands.hpp \ code/commands/folder-commands.hpp \ code/commands/help-commands.hpp \ code/commands/subscription-commands.hpp \ code/commands/synchronization-commands.hpp \ code/commands/user-join-commands.hpp \ code/commands/view-commands.hpp code/commands/application-commands.cpp000066400000000000000000000043511304552132100203760ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "commands/application-commands.hpp" Gobby::ApplicationCommands::ApplicationCommands( Gtk::Application& application, const ApplicationActions& actions, FileChooser& file_chooser, Preferences& preferences, CertificateManager& cert_manager) : m_application(application), m_file_chooser(file_chooser), m_preferences(preferences), m_cert_manager(cert_manager) { actions.preferences->signal_activate().connect( sigc::hide(sigc::mem_fun( *this, &ApplicationCommands::on_preferences))); actions.quit->signal_activate().connect( sigc::hide(sigc::mem_fun( *this, &ApplicationCommands::on_quit))); } void Gobby::ApplicationCommands::on_preferences() { Gtk::Window* parent = m_application.get_windows()[0]; if(!m_preferences_dialog.get()) { m_preferences_dialog = PreferencesDialog::create( *parent, m_file_chooser, m_preferences, m_cert_manager); } m_preferences_dialog->present(); } void Gobby::ApplicationCommands::on_quit() { // TODO: m_application.quit() produces a memory leak // since it results in the Gtk::Application destructor not running, // which will cause the settings not to be saved. //m_application.quit(); // Instead, hide all windows, which will cause a clean exit. std::vector windows = m_application.get_windows(); for(std::vector::iterator iter = windows.begin(); iter != windows.end(); ++iter) { (*iter)->hide(); } } code/commands/application-commands.hpp000066400000000000000000000034411304552132100204020ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_APPLICATION_COMMANDS_HPP_ #define _GOBBY_APPLICATION_COMMANDS_HPP_ #include "dialogs/preferences-dialog.hpp" #include "core/applicationactions.hpp" #include "core/filechooser.hpp" #include "core/preferences.hpp" #include "core/certificatemanager.hpp" #include #include #include namespace Gobby { class ApplicationCommands: public sigc::trackable { public: ApplicationCommands(Gtk::Application& application, const ApplicationActions& actions, FileChooser& file_chooser, Preferences& preferences, CertificateManager& cert_manager); protected: void on_preferences(); void on_quit(); Gtk::Application& m_application; FileChooser& m_file_chooser; Preferences& m_preferences; CertificateManager& m_cert_manager; std::unique_ptr m_preferences_dialog; }; } #endif // _GOBBY_APPLICATION_COMMANDS_HPP_ code/commands/auth-commands.cpp000066400000000000000000000263221304552132100170360ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "commands/auth-commands.hpp" #include "util/i18n.hpp" #include #include namespace { void show_error(const GError* error, Gobby::StatusBar& statusbar, InfXmlConnection* connection) { gchar* remote; g_object_get(connection, "remote-hostname", &remote, NULL); Glib::ustring short_message(Glib::ustring::compose( "Authentication failed for \"%1\"", remote)); g_free(remote); if(error->domain == inf_authentication_detail_error_quark()) { statusbar.add_error_message( short_message, inf_authentication_detail_strerror( InfAuthenticationDetailError( error->code))); } else { statusbar.add_error_message( short_message, error->message); } } } Gobby::AuthCommands::AuthCommands(Gtk::Window& parent, Browser& browser, StatusBar& statusbar, ConnectionManager& connection_manager, const Preferences& preferences): m_parent(parent), m_browser(browser), m_connection_manager(connection_manager), m_statusbar(statusbar), m_preferences(preferences) { GError* error = NULL; m_sasl_context = inf_sasl_context_new(&error); if(!m_sasl_context) { std::string error_message = std::string("SASL initialization error: ") + error->message; g_error_free(error); throw std::runtime_error(error_message); } inf_sasl_context_set_callback( m_sasl_context, &AuthCommands::sasl_callback_static, this, NULL); // Set SASL context for new and existing connections: m_connection_manager.set_sasl_context(m_sasl_context, "ANONYMOUS PLAIN"); g_signal_connect( G_OBJECT(m_browser.get_store()), "set-browser", G_CALLBACK(&AuthCommands::set_browser_callback_static), this); // Setup callback for existing browsers GtkTreeIter iter; GtkTreeModel* model = GTK_TREE_MODEL(m_browser.get_store()); for(gboolean have_entry = gtk_tree_model_get_iter_first(model, &iter); have_entry == TRUE; have_entry = gtk_tree_model_iter_next(model, &iter)) { InfBrowser* browser; gtk_tree_model_get( model, &iter, INF_GTK_BROWSER_MODEL_COL_BROWSER, &browser, -1); if(browser != NULL) { set_browser_callback(NULL, browser); g_object_unref(browser); } } } Gobby::AuthCommands::~AuthCommands() { m_connection_manager.set_sasl_context(NULL, NULL); inf_sasl_context_unref(m_sasl_context); for(RetryMap::iterator iter = m_retries.begin(); iter != m_retries.end(); ++iter) { g_signal_handler_disconnect(iter->first, iter->second.handle); } } void Gobby::AuthCommands::set_sasl_error(InfXmppConnection* connection, const gchar* message) { GError* error = g_error_new_literal( inf_authentication_detail_error_quark(), INF_AUTHENTICATION_DETAIL_ERROR_AUTHENTICATION_FAILED, message ); inf_xmpp_connection_set_sasl_error(connection, error); g_error_free(error); } void Gobby::AuthCommands::sasl_callback(InfSaslContextSession* session, InfXmppConnection* xmpp, Gsasl_property prop) { const Glib::ustring username = m_preferences.user.name; const std::string correct_password = m_preferences.user.password; const char* password; gsize password_len; gchar cmp; switch(prop) { case GSASL_ANONYMOUS_TOKEN: inf_sasl_context_session_set_property( session, GSASL_ANONYMOUS_TOKEN, username.c_str()); inf_sasl_context_session_continue(session, GSASL_OK); break; case GSASL_AUTHID: inf_sasl_context_session_set_property( session, GSASL_AUTHID, username.c_str()); inf_sasl_context_session_continue(session, GSASL_OK); break; case GSASL_PASSWORD: { RetryMap::iterator i = m_retries.find(xmpp); if(i == m_retries.end()) i = insert_retry_info(xmpp); RetryInfo& info(i->second); if(!info.last_password.empty()) { inf_sasl_context_session_set_property( session, GSASL_PASSWORD, info.last_password.c_str()); inf_sasl_context_session_continue(session, GSASL_OK); } else { // Query user for password g_assert(info.password_dialog == NULL); gchar* remote_id; g_object_get(G_OBJECT(xmpp), "remote-hostname", &remote_id, NULL); Glib::ustring remote_id_(remote_id); g_free(remote_id); std::unique_ptr dialog = PasswordDialog::create( m_parent, remote_id_, info.retries); info.password_dialog = dialog.release(); info.password_dialog->add_button( _("_Cancel"), Gtk::RESPONSE_CANCEL); info.password_dialog->add_button( _("_Ok"), Gtk::RESPONSE_ACCEPT); Gtk::Dialog& dlg = *info.password_dialog; dlg.signal_response().connect(sigc::bind( sigc::mem_fun( *this, &AuthCommands::on_response), session, xmpp)); info.password_dialog->present(); } } break; case GSASL_VALIDATE_ANONYMOUS: if(m_preferences.user.require_password) { inf_sasl_context_session_continue( session, GSASL_AUTHENTICATION_ERROR ); set_sasl_error(xmpp, _("Password required")); } else { inf_sasl_context_session_continue(session, GSASL_OK); } break; case GSASL_VALIDATE_SIMPLE: password = inf_sasl_context_session_get_property( session, GSASL_PASSWORD); /* length-independent string compare */ cmp = 0; password_len = strlen(password); for(unsigned i = 0; i < correct_password.size(); ++i) { if(i < password_len) cmp |= (password[i] ^ correct_password[i]); else cmp |= (0x00 ^ correct_password[i]); } if(password_len != correct_password.size()) cmp = 0xFF; if(cmp != 0) { inf_sasl_context_session_continue( session, GSASL_AUTHENTICATION_ERROR ); set_sasl_error(xmpp, _("Incorrect password")); } else { inf_sasl_context_session_continue(session, GSASL_OK); } break; default: inf_sasl_context_session_continue(session, GSASL_NO_CALLBACK); break; } } void Gobby::AuthCommands::on_response(int response_id, InfSaslContextSession* session, InfXmppConnection* xmpp) { RetryMap::iterator i = m_retries.find(xmpp); g_assert(i != m_retries.end()); RetryInfo& info(i->second); if(response_id == Gtk::RESPONSE_ACCEPT) info.last_password = info.password_dialog->get_password(); else info.last_password = ""; delete info.password_dialog; info.password_dialog = NULL; ++info.retries; if(info.last_password.empty()) { inf_sasl_context_session_continue(session, GSASL_NO_PASSWORD); } else { inf_sasl_context_session_set_property( session, GSASL_PASSWORD, info.last_password.c_str()); inf_sasl_context_session_continue(session, GSASL_OK); } } void Gobby::AuthCommands::set_browser_callback(InfBrowser* old_browser, InfBrowser* new_browser) { // TODO: Disconnect from the signal on destruction? if(new_browser != NULL && INFC_IS_BROWSER(new_browser)) { g_signal_connect( G_OBJECT(new_browser), "error", G_CALLBACK(browser_error_callback_static), this); } } void Gobby::AuthCommands::browser_error_callback(InfcBrowser* browser, GError* error) { // The Browser already displays errors inline, but we want // auth-related error messages to show up in the status bar. InfXmlConnection* connection = infc_browser_get_connection(browser); g_assert(INF_IS_XMPP_CONNECTION(connection)); InfXmppConnection* xmpp = INF_XMPP_CONNECTION(connection); RetryMap::iterator iter = m_retries.find(xmpp); if(iter == m_retries.end()) iter = insert_retry_info(xmpp); Glib::ustring& last_password(iter->second.last_password); Glib::ustring old_password; old_password.swap(last_password); if(error->domain == g_quark_from_static_string("INF_XMPP_CONNECTION_AUTH_ERROR")) { // Authentication failed for some reason, maybe because the // server aborted authentication. If we were querying a // password then close the dialog now. delete iter->second.password_dialog; iter->second.password_dialog = NULL; const GError* sasl_error = inf_xmpp_connection_get_sasl_error(xmpp); if(sasl_error != NULL && sasl_error->domain == inf_authentication_detail_error_quark()) { handle_error_detail(xmpp, sasl_error, old_password, last_password); } else if(sasl_error != NULL) { show_error(sasl_error, m_statusbar, connection); } else { show_error(error, m_statusbar, connection); } } else if(error->domain == inf_gsasl_error_quark()) { show_error(error, m_statusbar, connection); } } void Gobby::AuthCommands::handle_error_detail(InfXmppConnection* xmpp, const GError* detail_error, Glib::ustring& old_password, Glib::ustring& last_password) { GError* error = NULL; switch(detail_error->code) { case INF_AUTHENTICATION_DETAIL_ERROR_AUTHENTICATION_FAILED: inf_xmpp_connection_retry_sasl_authentication(xmpp, &error); break; case INF_AUTHENTICATION_DETAIL_ERROR_TRY_AGAIN: old_password.swap(last_password); inf_xmpp_connection_retry_sasl_authentication(xmpp, &error); break; default: show_error(detail_error, m_statusbar, INF_XML_CONNECTION(xmpp)); break; } if(error) { show_error(error, m_statusbar, INF_XML_CONNECTION(xmpp)); g_error_free(error); } } Gobby::AuthCommands::RetryMap::iterator Gobby::AuthCommands::insert_retry_info(InfXmppConnection* xmpp) { RetryMap::iterator iter = m_retries.insert( std::make_pair(xmpp, RetryInfo())).first; iter->second.retries = 0; iter->second.handle = g_signal_connect( G_OBJECT(xmpp), "notify::status", G_CALLBACK(on_notify_status_static), this); iter->second.password_dialog = NULL; return iter; } void Gobby::AuthCommands::on_notify_status(InfXmppConnection* connection) { InfXmlConnectionStatus status; g_object_get(G_OBJECT(connection), "status", &status, NULL); if(status != INF_XML_CONNECTION_OPENING) { RetryMap::iterator iter = m_retries.find(connection); g_signal_handler_disconnect(connection, iter->second.handle); delete iter->second.password_dialog; m_retries.erase(iter); } } code/commands/auth-commands.hpp000066400000000000000000000102231304552132100170340ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_AUTH_COMMANDS_HPP_ #define _GOBBY_AUTH_COMMANDS_HPP_ #include "dialogs/password-dialog.hpp" #include "core/browser.hpp" #include "core/statusbar.hpp" #include "core/preferences.hpp" #include #include namespace Gobby { class AuthCommands: public sigc::trackable { public: AuthCommands(Gtk::Window& parent, Browser& browser, StatusBar& statusbar, ConnectionManager& connection_manager, const Preferences& preferences); ~AuthCommands(); InfSaslContext* get_sasl_context() { return m_sasl_context; } protected: static void sasl_callback_static(InfSaslContextSession* session, Gsasl_property prop, gpointer session_data, gpointer user_data) { AuthCommands* auth = static_cast(user_data); auth->sasl_callback( session, INF_XMPP_CONNECTION(session_data), prop); } static void set_browser_callback_static(InfGtkBrowserModel*, GtkTreePath*, GtkTreeIter*, InfBrowser* old_browser, InfBrowser* new_browser, gpointer user_data) { AuthCommands* auth = static_cast(user_data); auth->set_browser_callback(old_browser, new_browser); } static void on_notify_status_static(GObject* connection_obj, GParamSpec*, gpointer user_data) { AuthCommands* auth = static_cast(user_data); auth->on_notify_status(INF_XMPP_CONNECTION(connection_obj)); } static void browser_error_callback_static(InfcBrowser* browser, gpointer error_ptr, gpointer user_data) { AuthCommands* auth = static_cast(user_data); GError* error = static_cast(error_ptr); auth->browser_error_callback(browser, error); } void set_sasl_error(InfXmppConnection* connection, const gchar* message); void sasl_callback(InfSaslContextSession* session, InfXmppConnection* xmpp, Gsasl_property prop); void set_browser_callback(InfBrowser* old_browser, InfBrowser* new_browser); void browser_error_callback(InfcBrowser* browser, GError* error); void handle_error_detail(InfXmppConnection* xmpp, const GError* detail_error, Glib::ustring& old_password, Glib::ustring& last_password); struct RetryInfo { unsigned int retries; Glib::ustring last_password; gulong handle; PasswordDialog* password_dialog; }; typedef std::map RetryMap; RetryMap::iterator insert_retry_info(InfXmppConnection* xmpp); void on_notify_status(InfXmppConnection* connection); void on_response(int response_id, InfSaslContextSession* session, InfXmppConnection* xmpp); Gtk::Window& m_parent; Browser& m_browser; StatusBar& m_statusbar; ConnectionManager& m_connection_manager; const Preferences& m_preferences; InfSaslContext* m_sasl_context; RetryMap m_retries; }; } #endif // _GOBBY_AUTH_COMMANDS_HPP_ code/commands/autosave-commands.cpp000066400000000000000000000220121304552132100177140ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "commands/autosave-commands.hpp" #include "operations/operation-save.hpp" #include "core/sessionuserview.hpp" #include #include class Gobby::AutosaveCommands::Info { public: Info(AutosaveCommands& commands, TextSessionView& view): m_commands(commands), m_view(view), m_save_op(NULL) { GtkSourceBuffer* buffer = m_view.get_text_buffer(); m_modified_changed_handler = g_signal_connect_after( G_OBJECT(buffer), "modified-changed", G_CALLBACK(on_modified_changed_static), this); // We can't get this correct, so we assume the document was // synchronized to disk at the current time. If it has been // modified, we schedule a first autosave. m_sync_time = std::time(NULL); Operations& operations = m_commands.m_operations; OperationSave* save_op = operations.get_save_operation_for_document(view); if(save_op != NULL) begin_save_operation(save_op); else if(gtk_text_buffer_get_modified(GTK_TEXT_BUFFER(buffer))) schedule(); } ~Info() { GtkSourceBuffer* buffer = m_view.get_text_buffer(); g_signal_handler_disconnect(G_OBJECT(buffer), m_modified_changed_handler); m_timeout_handler.disconnect(); } // Called by AutosaveCommands when the timeout interval has changed. // Just reschedule the timeout. void reschedule() { if(m_timeout_handler.connected()) { m_timeout_handler.disconnect(); schedule(); } } // Called by AutosaveCommands void begin_save_operation(OperationSave* save_op) { g_assert(m_save_op == NULL); // The document is already being saved, so we don't // need to autosave anymore. Reschedule autosave when save // operation finished. if(m_timeout_handler.connected()) m_timeout_handler.disconnect(); m_save_op = save_op; m_save_op->signal_finished().connect( sigc::mem_fun( *this, &Info::on_save_operation_finished)); } protected: void on_modified_changed() { if(m_save_op == NULL) { GtkTextBuffer* buffer = GTK_TEXT_BUFFER(m_view.get_text_buffer()); if(!gtk_text_buffer_get_modified(buffer)) m_timeout_handler.disconnect(); else { // Until now the document on disk is in sync // with the in-memory buffer. Now the document // has changed, so schedule an autosave. m_sync_time = std::time(NULL); schedule(); } } } void schedule() { g_assert(!m_timeout_handler.connected()); g_assert(m_save_op == NULL); // Don't schedule a timeout in case the document has no entry // in the document info storage. This means we don't have an // uri yet where to save the document. However, we // automatically retry when the document is assigned an URI, // since the modification flag will change with this anyway. const std::string& key = m_view.get_info_storage_key(); const DocumentInfoStorage::Info* info = m_commands.m_info_storage.get_info(key); if(!info || info->uri.empty()) return; guint elapsed_seconds = std::time(NULL) - m_sync_time; guint autosave_interval = 60 * m_commands.m_preferences.editor.autosave_interval; if(elapsed_seconds > autosave_interval) { on_timeout(); } else { m_timeout_handler = Glib::signal_timeout().connect_seconds( sigc::mem_fun( *this, &Info::on_timeout), autosave_interval - elapsed_seconds); } } void on_save_operation_finished(bool success) { GtkTextBuffer* buffer = GTK_TEXT_BUFFER(m_view.get_text_buffer()); if(success) m_sync_time = m_save_op->get_start_time(); m_save_op = NULL; // Schedule the next save operation in case the buffer has // been modified since the save operation was started. if(gtk_text_buffer_get_modified(buffer)) schedule(); } bool on_timeout() { const std::string& key = m_view.get_info_storage_key(); const DocumentInfoStorage::Info* info = m_commands.m_info_storage.get_info(key); // Might have been removed from info in the meanwhile, so // don't assert here. if(info != NULL) { Glib::RefPtr file = Gio::File::create_for_uri(info->uri); m_commands.m_operations.save_document( m_view, file, info->encoding, info->eol_style); g_assert(m_save_op != NULL); // Set sync time to operation's start time even though // we don't know yet whether the operation will fail, // because otherwise we would try to save the // document the whole time. This way, we simply retry // when the timeout triggers again. m_sync_time = m_save_op->get_start_time(); } return false; } private: static void on_modified_changed_static(GtkTextBuffer* buffer, gpointer user_data) { static_cast(user_data)->on_modified_changed(); } AutosaveCommands& m_commands; TextSessionView& m_view; gulong m_modified_changed_handler; sigc::connection m_timeout_handler; OperationSave* m_save_op; std::time_t m_sync_time; }; Gobby::AutosaveCommands::AutosaveCommands(const Folder& folder, Operations& operations, const DocumentInfoStorage& storage, const Preferences& preferences): m_folder(folder), m_operations(operations), m_info_storage(storage), m_preferences(preferences) { m_folder.signal_document_added().connect( sigc::mem_fun(*this, &AutosaveCommands::on_document_added)); m_folder.signal_document_removed().connect( sigc::mem_fun(*this, &AutosaveCommands::on_document_removed)); m_operations.signal_begin_save_operation().connect( sigc::mem_fun( *this, &AutosaveCommands::on_begin_save_operation)); m_preferences.editor.autosave_enabled.signal_changed().connect( sigc::mem_fun( *this, &AutosaveCommands::on_autosave_enabled_changed)); m_preferences.editor.autosave_interval.signal_changed().connect( sigc::mem_fun( *this, &AutosaveCommands::on_autosave_interval_changed)); // Create autosave infos for initial documents on_autosave_enabled_changed(); } Gobby::AutosaveCommands::~AutosaveCommands() { for(InfoMap::iterator iter = m_info_map.begin(); iter != m_info_map.end(); ++ iter) { delete iter->second; } } void Gobby::AutosaveCommands::on_document_added(SessionView& view) { if(m_preferences.editor.autosave_enabled) { // We can only save text views: TextSessionView* text_view = dynamic_cast(&view); if(text_view) { g_assert(m_info_map.find(text_view) == m_info_map.end()); m_info_map[text_view] = new Info(*this, *text_view); } } } void Gobby::AutosaveCommands::on_document_removed(SessionView& view) { if(m_preferences.editor.autosave_enabled) { TextSessionView* text_view = dynamic_cast(&view); if(text_view) { InfoMap::iterator iter = m_info_map.find(text_view); g_assert(iter != m_info_map.end()); delete iter->second; m_info_map.erase(iter); } } } void Gobby::AutosaveCommands::on_begin_save_operation( OperationSave* operation) { TextSessionView* view = operation->get_view(); // Save operation just started, document must be present g_assert(view != NULL); if(m_preferences.editor.autosave_enabled) { InfoMap::iterator iter = m_info_map.find(view); g_assert(iter != m_info_map.end()); iter->second->begin_save_operation(operation); } } void Gobby::AutosaveCommands::on_autosave_enabled_changed() { if(m_preferences.editor.autosave_enabled) { for(unsigned int i = 0; i < static_cast(m_folder.get_n_pages()); ++i) { // TODO: Add convenience API to folder, so that we // don't need to know here that it actually contains // SessionUserViews. const SessionUserView* userview = static_cast( m_folder.get_nth_page(i)); SessionView& view = userview->get_session_view(); TextSessionView* text_view = dynamic_cast(&view); if(text_view) { m_info_map[text_view] = new Info(*this, *text_view); } } } else { for(InfoMap::iterator iter = m_info_map.begin(); iter != m_info_map.end(); ++ iter) { delete iter->second; } m_info_map.clear(); } } void Gobby::AutosaveCommands::on_autosave_interval_changed() { // Propagate to infos for(InfoMap::iterator iter = m_info_map.begin(); iter != m_info_map.end(); ++ iter) { return iter->second->reschedule(); } } code/commands/autosave-commands.hpp000066400000000000000000000035041304552132100177260ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_AUTOSAVE_COMMANDS_HPP_ #define _GOBBY_AUTOSAVE_COMMANDS_HPP_ #include #include "operations/operations.hpp" #include "core/folder.hpp" #include "core/documentinfostorage.hpp" #include "core/preferences.hpp" namespace Gobby { class AutosaveCommands: public sigc::trackable { public: AutosaveCommands(const Folder& folder, Operations& operations, const DocumentInfoStorage& storage, const Preferences& preferences); ~AutosaveCommands(); protected: void on_document_added(SessionView& view); void on_document_removed(SessionView& view); void on_begin_save_operation(OperationSave* operation); void on_autosave_enabled_changed(); void on_autosave_interval_changed(); const Folder& m_folder; Operations& m_operations; const DocumentInfoStorage& m_info_storage; const Preferences& m_preferences; class Info; typedef std::map InfoMap; InfoMap m_info_map; }; } #endif // _GOBBY_AUTOSAVE_COMMANDS_HPP_ code/commands/browser-commands.cpp000066400000000000000000000423671304552132100175670ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "commands/browser-commands.hpp" #include "util/i18n.hpp" #include #include namespace { void count_available_users_func(InfUser* user, gpointer user_data) { if(inf_user_get_status(user) != INF_USER_UNAVAILABLE && ~inf_user_get_flags(user) & INF_USER_LOCAL) { ++(*(unsigned int*)user_data); } } unsigned int count_available_users(InfUserTable* user_table) { // Count available remote users unsigned int n_users = 0; inf_user_table_foreach_user( user_table, count_available_users_func, &n_users); return n_users; } class ParameterProvider: public Gobby::UserJoin::ParameterProvider { public: ParameterProvider(const Gobby::Preferences& preferences): m_preferences(preferences) { } virtual std::vector get_user_join_parameters(); const Gobby::Preferences& m_preferences; }; std::vector ParameterProvider::get_user_join_parameters() { std::vector params; const GParameter name_param = { "name", { 0 } }; params.push_back(name_param); const GParameter status_param = { "status", { 0 } }; params.push_back(status_param); g_value_init(¶ms[0].value, G_TYPE_STRING); g_value_init(¶ms[1].value, INF_TYPE_USER_STATUS); const Glib::ustring& name = m_preferences.user.name; g_value_set_string(¶ms[0].value, name.c_str()); g_value_set_enum(¶ms[1].value, INF_USER_INACTIVE); return params; } } class Gobby::BrowserCommands::BrowserInfo { public: BrowserInfo(BrowserCommands& commands, InfBrowser* browser); ~BrowserInfo(); InfBrowser* get_browser() { return m_browser; } void set_pending_chat(InfSessionProxy* proxy); void clear_pending_chat(); void check_pending_chat(); private: static void on_notify_status_static(GObject* object, GParamSpec* pspec, gpointer user_data) { static_cast(user_data)->on_notify_status( INF_BROWSER(object)); } static void on_add_available_user_static(InfUserTable* user_table, InfUser* user, gpointer user_data) { static_cast(user_data)->check_pending_chat(); } BrowserCommands& m_commands; std::unique_ptr m_userjoin; InfBrowser* m_browser; gulong m_notify_status_handler; InfSessionProxy* m_pending_chat; gulong m_add_available_user_handler; }; class Gobby::BrowserCommands::RequestInfo { friend class BrowserCommands; public: RequestInfo(BrowserCommands& commands, InfBrowser* browser, InfBrowserIter* iter, StatusBar& status_bar); ~RequestInfo(); InfBrowser* get_browser() { return m_browser; } void set_request(InfRequest* request); private: static void on_node_finished_static(InfRequest* request, const InfRequestResult* result, const GError* error, gpointer user_data) { const InfBrowserIter* iter = NULL; RequestInfo* info = static_cast(user_data); if(error == NULL) { inf_request_result_get_subscribe_session( result, NULL, &iter, NULL); g_assert(iter == NULL || iter->node == info->m_iter.node); g_assert(iter == NULL || iter->node_id == info->m_iter.node_id); } info->m_commands.on_finished( INF_REQUEST(request), info->m_browser, iter, error); } static void on_chat_finished_static(InfRequest* request, const InfRequestResult* result, const GError* error, gpointer user_data) { RequestInfo* info = static_cast(user_data); info->m_commands.on_finished( INF_REQUEST(request), info->m_browser, NULL, error); } BrowserCommands& m_commands; InfBrowser* m_browser; InfBrowserIter m_iter; StatusBar& m_status_bar; StatusBar::MessageHandle m_handle; InfRequest* m_request; gulong m_finished_handler; }; Gobby::BrowserCommands::BrowserInfo::BrowserInfo(BrowserCommands& cmds, InfBrowser* browser): m_commands(cmds), m_browser(browser), m_pending_chat(NULL) { m_notify_status_handler = g_signal_connect( m_browser, "notify::status", G_CALLBACK(on_notify_status_static), &cmds); g_object_ref(browser); } Gobby::BrowserCommands::BrowserInfo::~BrowserInfo() { if(m_pending_chat != NULL) { InfSession* session; g_object_get(G_OBJECT(m_pending_chat), "session", &session, NULL); InfUserTable* table = inf_session_get_user_table(session); g_signal_handler_disconnect( table, m_add_available_user_handler); g_object_unref(session); g_object_unref(m_pending_chat); } g_signal_handler_disconnect(m_browser, m_notify_status_handler); g_object_unref(m_browser); } void Gobby::BrowserCommands::BrowserInfo::set_pending_chat(InfSessionProxy* proxy) { g_assert(m_pending_chat == NULL); m_pending_chat = proxy; g_object_ref(proxy); InfSession* session; g_object_get(G_OBJECT(proxy), "session", &session, NULL); InfUserTable* table = inf_session_get_user_table(session); m_add_available_user_handler = g_signal_connect( G_OBJECT(table), "add-available-user", G_CALLBACK(on_add_available_user_static), this); g_object_unref(session); // Attempt a user join already here before the document is added to // the folder manager so that we can correctly show the document when // enough users are available. Otherwise if no party would join a user // before other users show up, the chat would never be shown. std::unique_ptr provider( new ParameterProvider(m_commands.m_preferences)); m_userjoin.reset(new UserJoin(m_browser, NULL, proxy, std::move(provider))); check_pending_chat(); } void Gobby::BrowserCommands::BrowserInfo::clear_pending_chat() { if(m_pending_chat != NULL) { InfSession* session; g_object_get(G_OBJECT(m_pending_chat), "session", &session, NULL); InfUserTable* table = inf_session_get_user_table(session); g_signal_handler_disconnect( table, m_add_available_user_handler); m_userjoin.reset(NULL); g_object_unref(m_pending_chat); m_pending_chat = NULL; g_object_unref(session); } } void Gobby::BrowserCommands::BrowserInfo::check_pending_chat() { g_assert(m_pending_chat != NULL); InfSession* session; g_object_get(G_OBJECT(m_pending_chat), "session", &session, NULL); InfUserTable* table = inf_session_get_user_table(session); if(count_available_users(table) > 0) { g_signal_handler_disconnect( table, m_add_available_user_handler); m_commands.m_folder_manager.add_document( m_browser, NULL, m_pending_chat, &m_userjoin); g_object_unref(m_pending_chat); m_pending_chat = NULL; } g_object_unref(session); } Gobby::BrowserCommands::RequestInfo::RequestInfo(BrowserCommands& commands, InfBrowser* browser, InfBrowserIter* iter, StatusBar& status_bar): m_commands(commands), m_browser(browser), m_status_bar(status_bar), m_request(NULL), m_finished_handler(0) { if(iter) { m_iter = *iter; m_handle = m_status_bar.add_info_message( Glib::ustring::compose( _("Subscribing to %1..."), Glib::ustring( inf_browser_get_node_name( browser, iter)))); } else { InfXmlConnection* connection = infc_browser_get_connection(INFC_BROWSER(browser)); gchar* remote_hostname; g_object_get(G_OBJECT(connection), "remote-hostname", &remote_hostname, NULL); m_handle = m_status_bar.add_info_message( Glib::ustring::compose( _("Subscribing to chat on %1..."), remote_hostname)); g_free(remote_hostname); } } Gobby::BrowserCommands::RequestInfo::~RequestInfo() { m_status_bar.remove_message(m_handle); if(m_request != NULL) { g_signal_handler_disconnect(m_request, m_finished_handler); g_object_unref(m_request); } } void Gobby::BrowserCommands::RequestInfo::set_request(InfRequest* request) { g_assert(m_request == NULL); m_finished_handler = g_signal_handler_find( request, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, this); g_assert(m_finished_handler != 0); } Gobby::BrowserCommands::BrowserCommands(Browser& browser, FolderManager& folder_manager, StatusBar& status_bar, Operations& operations, const Preferences& preferences): m_browser(browser), m_folder_manager(folder_manager), m_operations(operations), m_status_bar(status_bar), m_preferences(preferences) { m_browser.signal_connect().connect( sigc::mem_fun(*this, &BrowserCommands::on_connect)); m_browser.signal_activate().connect( sigc::mem_fun(*this, &BrowserCommands::on_activate)); m_set_browser_handler = g_signal_connect( browser.get_store(), "set-browser", G_CALLBACK(on_set_browser_static), this); // Add already existing browsers GtkTreeIter iter; GtkTreeModel* treemodel = GTK_TREE_MODEL(browser.get_store()); for(gboolean have_entry = gtk_tree_model_get_iter_first(treemodel, &iter); have_entry == TRUE; have_entry = gtk_tree_model_iter_next(treemodel, &iter)) { InfBrowser* browser; gtk_tree_model_get( treemodel, &iter, INF_GTK_BROWSER_MODEL_COL_BROWSER, &browser, -1); InfBrowserStatus browser_status; g_object_get( G_OBJECT(browser), "status", &browser_status, NULL); m_browser_map[browser] = new BrowserInfo(*this, browser); if(browser_status == INF_BROWSER_OPEN) if(!create_chat_document(browser)) subscribe_chat(browser); g_object_unref(browser); } } Gobby::BrowserCommands::~BrowserCommands() { for(RequestMap::iterator iter = m_request_map.begin(); iter != m_request_map.end(); ++ iter) { delete iter->second; } for(BrowserMap::iterator iter = m_browser_map.begin(); iter != m_browser_map.end(); ++iter) { delete iter->second; } g_signal_handler_disconnect(m_browser.get_store(), m_set_browser_handler); } void Gobby::BrowserCommands::on_set_browser(InfGtkBrowserModel* model, GtkTreeIter* iter, InfBrowser* old_browser, InfBrowser* new_browser) { if(old_browser != NULL) { // Find by browser in case old_browser has its connection // reset. BrowserMap::iterator iter = m_browser_map.find(old_browser); g_assert(iter != m_browser_map.end()); delete iter->second; m_browser_map.erase(iter); } if(new_browser != NULL) { g_assert(m_browser_map.find(new_browser) == m_browser_map.end()); InfBrowserStatus browser_status; g_object_get( G_OBJECT(new_browser), "status", &browser_status, NULL); m_browser_map[new_browser] = new BrowserInfo(*this, new_browser); if(browser_status == INF_BROWSER_OPEN) if(!create_chat_document(new_browser)) subscribe_chat(new_browser); } } void Gobby::BrowserCommands::on_notify_status(InfBrowser* browser) { InfXmlConnection* connection; InfXmlConnectionStatus status; InfBrowserStatus browser_status; const BrowserMap::iterator iter = m_browser_map.find(browser); g_assert(iter != m_browser_map.end()); g_object_get(G_OBJECT(browser), "status", &browser_status, NULL); switch(browser_status) { case INF_BROWSER_CLOSED: iter->second->clear_pending_chat(); // Close connection if browser got disconnected. This for // example happens when the server does not send an initial // welcome message. if(INFC_IS_BROWSER(browser)) { connection = infc_browser_get_connection( INFC_BROWSER(browser)); g_object_get(G_OBJECT(connection), "status", &status, NULL); if(status != INF_XML_CONNECTION_CLOSED && status != INF_XML_CONNECTION_CLOSING) { inf_xml_connection_close(connection); } } break; case INF_BROWSER_OPENING: break; case INF_BROWSER_OPEN: if(!create_chat_document(browser)) subscribe_chat(browser); break; default: g_assert_not_reached(); break; } } void Gobby::BrowserCommands::subscribe_chat(InfBrowser* browser) { if(INFC_IS_BROWSER(browser)) { std::unique_ptr info(new RequestInfo( *this, INF_BROWSER(browser), NULL, m_status_bar)); InfRequest* request = INF_REQUEST( infc_browser_subscribe_chat( INFC_BROWSER(browser), RequestInfo::on_chat_finished_static, info.get())); if(request != NULL) { info->set_request(request); g_assert(m_request_map.find(request) == m_request_map.end()); m_request_map[request] = info.release(); } } else if(INFD_IS_DIRECTORY(browser)) { infd_directory_enable_chat(INFD_DIRECTORY(browser), TRUE); create_chat_document(browser); } } bool Gobby::BrowserCommands::create_chat_document(InfBrowser* browser) { // Get the chat session from the browser InfSessionProxy* proxy = NULL; if(INFC_IS_BROWSER(browser)) { proxy = INF_SESSION_PROXY( infc_browser_get_chat_session(INFC_BROWSER(browser))); } else if(INFD_IS_DIRECTORY(browser)) { proxy = INF_SESSION_PROXY( infd_directory_get_chat_session( INFD_DIRECTORY(browser))); } // Chat session not available if(!proxy) return false; // First, check whether there is already a document for this // chat session. If there is, then don't create another one. InfSession* session; g_object_get(G_OBJECT(proxy), "session", &session, NULL); SessionView* view = m_folder_manager.get_chat_folder().lookup_document(session); g_object_unref(session); g_assert(view == NULL); // Can it happen? if(view) return true; // Document exists already if(INFC_IS_BROWSER(browser)) { // For clients, just add the document m_folder_manager.add_document( browser, NULL, proxy, NULL); return true; } else if(INFD_IS_DIRECTORY(browser)) { // For our local chat, only show it if there is at least // one remote user, otherwise wait until users show up. BrowserMap::iterator iter = m_browser_map.find(browser); g_assert(iter != m_browser_map.end()); iter->second->set_pending_chat(proxy); return true; } // This cannot actually happen, but return to silence compiler // warnings g_assert_not_reached(); return false; } void Gobby::BrowserCommands::on_connect(const Glib::ustring& hostname) { m_operations.subscribe_path(hostname); } void Gobby::BrowserCommands::on_activate(InfBrowser* browser, InfBrowserIter* iter) { InfSessionProxy* proxy = inf_browser_get_session(browser, iter); if(proxy != NULL) { InfSession* session; g_object_get(G_OBJECT(proxy), "session", &session, NULL); SessionView* view = m_folder_manager.lookup_document(session); g_object_unref(session); if(view != NULL) { m_folder_manager.switch_to_document(*view); } else { m_folder_manager.add_document(browser, iter, proxy, NULL); } } else { InfRequest* request = inf_browser_get_pending_request( browser, iter, "subscribe-session"); // If there is already a request don't re-request if(request == NULL) { std::unique_ptr info(new RequestInfo( *this, browser, iter, m_status_bar)); request = INF_REQUEST( inf_browser_subscribe( browser, iter, RequestInfo::on_node_finished_static, info.get())); if(request != NULL) { info->set_request(request); g_assert(m_request_map.find(request) == m_request_map.end()); m_request_map[request] = info.release(); } } } } void Gobby::BrowserCommands::on_finished(InfRequest* request, InfBrowser* browser, const InfBrowserIter* iter, const GError* error) { RequestMap::iterator map_iter = m_request_map.find(request); if(map_iter != m_request_map.end()) { delete map_iter->second; m_request_map.erase(map_iter); } if(error != NULL) { m_status_bar.add_error_message( _("Subscription failed"), error->message); } else if(iter != NULL) { InfSessionProxy* proxy = inf_browser_get_session(browser, iter); g_assert(proxy != NULL); m_folder_manager.add_document(browser, iter, proxy, NULL); } else { // For InfdDirectory we do not make a request for subscribing // to the chat. const bool created = create_chat_document(browser); g_assert(created); // Must work now } } code/commands/browser-commands.hpp000066400000000000000000000052341304552132100175640ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_BROWSER_COMMANDS_HPP_ #define _GOBBY_BROWSER_COMMANDS_HPP_ #include "operations/operations.hpp" #include "core/browser.hpp" #include "core/statusbar.hpp" #include namespace Gobby { class BrowserCommands: public sigc::trackable { public: BrowserCommands(Browser& browser, FolderManager& folder_manager, StatusBar& status_bar, Operations& operations, const Preferences& preferences); ~BrowserCommands(); protected: static void on_set_browser_static(InfGtkBrowserModel* model, GtkTreePath* path, GtkTreeIter* iter, InfBrowser* old_browser, InfBrowser* new_browser, gpointer user_data) { static_cast(user_data)-> on_set_browser(model, iter, old_browser, new_browser); } void on_set_browser(InfGtkBrowserModel* model, GtkTreeIter* iter, InfBrowser* old_browser, InfBrowser* new_browser); void on_notify_status(InfBrowser* browser); void subscribe_chat(InfBrowser* browser); bool create_chat_document(InfBrowser* browser); void on_connect(const Glib::ustring& hostname); void on_activate(InfBrowser* browser, InfBrowserIter* iter); void on_finished(InfRequest* request, InfBrowser* browser, const InfBrowserIter* iter, const GError* error); Browser& m_browser; FolderManager& m_folder_manager; StatusBar& m_status_bar; Operations& m_operations; const Preferences& m_preferences; gulong m_set_browser_handler; class BrowserInfo; typedef std::map BrowserMap; BrowserMap m_browser_map; class RequestInfo; typedef std::map RequestMap; RequestMap m_request_map; }; } #endif // _GOBBY_BROWSER_COMMANDS_HPP_ code/commands/browser-context-commands.cpp000066400000000000000000000437651304552132100212540ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "commands/browser-context-commands.hpp" #include "operations/operation-open-multiple.hpp" #include "dialogs/connection-info-dialog.hpp" #include "util/file.hpp" #include "util/i18n.hpp" #include #include #include #include #include #include // TODO: Use file tasks for the commands, once we made them public namespace { std::string make_safe_filename(const Glib::ustring& cn) { Glib::ustring output(cn); for(Glib::ustring::iterator iter = output.begin(); iter != output.end(); ) { if(Glib::Unicode::isspace(*iter) || *iter == '/' || *iter == '\\' || *iter == ':' || *iter == '?') { iter = output.erase(iter); } else { ++iter; } } return output; } bool check_file(const std::string& filename) { return Glib::file_test(filename, Glib::FILE_TEST_EXISTS); } std::string make_certificate_filename(const Glib::ustring& cn, const Glib::ustring& hostname) { const std::string basename = make_safe_filename(hostname) + "-" + make_safe_filename(cn); const std::string prefname = Gobby::config_filename(basename + ".pem"); if(!check_file(prefname)) return prefname; for(unsigned int i = 2; i < 10000; ++i) { const std::string altname = Gobby::config_filename( basename + "-" + Glib::ustring::compose( "%1", i) + ".pem"); if(!check_file(altname)) return altname; } throw std::runtime_error( Gobby::_("Could not find a location " "where to store the certificate")); } } Gobby::BrowserContextCommands::BrowserContextCommands( Gtk::Window& parent, InfIo* io, Browser& browser, FileChooser& chooser, Operations& operations, const CertificateManager& cert_manager, Preferences& prefs) : m_parent(parent), m_io(io), m_browser(browser), m_file_chooser(chooser), m_operations(operations), m_cert_manager(cert_manager), m_preferences(prefs), m_popup_menu(NULL), m_action_group(Gio::SimpleActionGroup::create()), m_action_remove(m_action_group->add_action("remove")), m_action_connection_info(m_action_group->add_action("connection-info")), m_action_disconnect(m_action_group->add_action("disconnect")), m_action_create_account(m_action_group->add_action("create-account")), m_action_create_document( m_action_group->add_action("create-document")), m_action_create_directory( m_action_group->add_action("create-directory")), m_action_open_document(m_action_group->add_action("open-document")), m_action_permissions(m_action_group->add_action("permissions")), m_action_delete(m_action_group->add_action("delete")) { g_object_ref(io); m_populate_popup_handler = g_signal_connect( m_browser.get_view(), "populate-popup", G_CALLBACK(on_populate_popup_static), this); gtk_widget_insert_action_group( GTK_WIDGET(m_browser.get_view()), "browser", G_ACTION_GROUP(m_action_group->gobj())); m_action_remove->signal_activate().connect( sigc::mem_fun(*this, &BrowserContextCommands::on_remove)); m_action_connection_info->signal_activate().connect( sigc::mem_fun(*this, &BrowserContextCommands::on_connection_info)); m_action_disconnect->signal_activate().connect( sigc::mem_fun(*this, &BrowserContextCommands::on_disconnect)); m_action_create_account->signal_activate().connect( sigc::mem_fun(*this, &BrowserContextCommands::on_create_account)); m_action_create_document->signal_activate().connect( sigc::bind(sigc::mem_fun(*this, &BrowserContextCommands::on_new), false)); m_action_create_directory->signal_activate().connect( sigc::bind(sigc::mem_fun(*this, &BrowserContextCommands::on_new), true)); m_action_open_document->signal_activate().connect( sigc::mem_fun(*this, &BrowserContextCommands::on_open)); m_action_permissions->signal_activate().connect( sigc::mem_fun(*this, &BrowserContextCommands::on_permissions)); m_action_delete->signal_activate().connect( sigc::mem_fun(*this, &BrowserContextCommands::on_delete)); } Gobby::BrowserContextCommands::~BrowserContextCommands() { g_signal_handler_disconnect(m_browser.get_view(), m_populate_popup_handler); g_object_unref(m_io); } void Gobby::BrowserContextCommands::on_populate_popup(Gtk::Menu* menu) { // TODO: Can this happen? Should we close the old popup here? g_assert(m_popup_menu == NULL); g_assert(m_popup_watch.get() == NULL); // Cancel previous dialogs m_dialog.reset(NULL); m_dialog_watch.reset(NULL); InfBrowser* browser; InfBrowserIter iter; InfBrowserStatus status; if(!m_browser.get_selected_browser(&browser)) return; g_object_get(G_OBJECT(browser), "status", &status, NULL); const bool has_iter = m_browser.get_selected_iter(browser, &iter); // TODO: At the moment, action handlers access the browser item using // the m_popup_watch variable. We should change it such that // the selected item is passed as an action parameter, as two // strings: The browser store item name, and the path to the selected // node. This could then also easily allow browser actions to be // invoked via d-bus. Glib::RefPtr builder = Gtk::Builder::create_from_resource( "/de/0x539/gobby/ui/browser-context-menu.ui"); if(!has_iter) { InfBrowserStatus browser_status; g_object_get(G_OBJECT(browser), "status", &browser_status, NULL); Glib::RefPtr menu_model = Glib::RefPtr::cast_dynamic( builder->get_object( "browser-context-menu-inactive")); if(browser_status == INF_BROWSER_CLOSED) { menu->bind_model(menu_model, true); } else { // No popup menu return; } } else { InfBrowserIter dummy_iter = iter; const bool is_subdirectory = inf_browser_is_subdirectory(browser, &iter); const bool is_toplevel = !inf_browser_get_parent(browser, &dummy_iter); m_action_connection_info->set_enabled( is_toplevel && status == INF_BROWSER_OPEN); m_action_disconnect->set_enabled( is_toplevel && INFC_IS_BROWSER(browser)); m_action_create_account->set_enabled( is_toplevel && INFC_IS_BROWSER(browser)); m_action_create_document->set_enabled(is_subdirectory); m_action_create_directory->set_enabled(is_subdirectory); m_action_open_document->set_enabled(is_subdirectory); m_action_delete->set_enabled(!is_toplevel); Glib::RefPtr menu_model = Glib::RefPtr::cast_dynamic( builder->get_object( "browser-context-menu-active")); menu->bind_model(menu_model, true); } // Watch the node, and close the popup menu when the node // it refers to is removed. m_popup_watch.reset(new NodeWatch(browser, has_iter ? &iter : NULL)); m_popup_watch->signal_node_removed().connect(sigc::mem_fun( *this, &BrowserContextCommands::on_menu_node_removed)); m_popup_menu = menu; menu->signal_selection_done().connect( sigc::mem_fun( *this, &BrowserContextCommands::on_popdown)); } void Gobby::BrowserContextCommands::on_menu_node_removed() { g_assert(m_popup_menu != NULL); // This calls selection_done, causing the watch to be removed // and the m_popup_menu pointer to be reset. m_popup_menu->popdown(); } void Gobby::BrowserContextCommands::on_popdown() { m_popup_menu = NULL; m_popup_watch.reset(NULL); } // Action handlers void Gobby::BrowserContextCommands::on_remove(const Glib::VariantBase& param) { InfBrowser* browser = m_popup_watch->get_browser(); InfBrowserStatus status; g_object_get(G_OBJECT(browser), "status", &status, NULL); g_assert(status == INF_BROWSER_CLOSED); // Reset the watch explicitly here in case we drop the last reference // to the browser. m_popup_watch.reset(NULL); m_browser.remove_browser(browser); } void Gobby::BrowserContextCommands::on_connection_info( const Glib::VariantBase& param) { InfBrowser* browser = m_popup_watch->get_browser(); m_dialog = ConnectionInfoDialog::create(m_parent, browser); m_dialog->add_button(_("_Close"), Gtk::RESPONSE_CLOSE); m_dialog->signal_response().connect( sigc::mem_fun(*this, &BrowserContextCommands:: on_connection_info_response)); m_dialog->present(); } void Gobby::BrowserContextCommands::on_disconnect( const Glib::VariantBase& param) { InfcBrowser* browser = INFC_BROWSER(m_popup_watch->get_browser()); InfXmlConnection* connection = infc_browser_get_connection(browser); InfXmlConnectionStatus status; g_object_get(G_OBJECT(connection), "status", &status, NULL); if(status != INF_XML_CONNECTION_CLOSED && status != INF_XML_CONNECTION_CLOSING) { inf_xml_connection_close(connection); } } void Gobby::BrowserContextCommands::on_create_account( const Glib::VariantBase& param) { InfBrowser* browser = m_popup_watch->get_browser(); InfGtkAccountCreationDialog* dlg = inf_gtk_account_creation_dialog_new( m_parent.gobj(), static_cast(0), m_io, browser); std::unique_ptr account_creation_dialog( Glib::wrap(GTK_DIALOG(dlg))); account_creation_dialog->add_button(_("_Close"), Gtk::RESPONSE_CLOSE); account_creation_dialog->signal_response().connect( sigc::mem_fun(*this, &BrowserContextCommands::on_create_account_response)); g_signal_connect( G_OBJECT(account_creation_dialog->gobj()), "account-created", G_CALLBACK(on_account_created_static), this); m_dialog = std::move(account_creation_dialog); m_dialog->present(); } void Gobby::BrowserContextCommands::on_new(const Glib::VariantBase& param, bool directory) { InfBrowser* browser = m_popup_watch->get_browser(); const InfBrowserIter iter = *m_popup_watch->get_browser_iter(); m_dialog_watch.reset(new NodeWatch(browser, &iter)); m_dialog_watch->signal_node_removed().connect(sigc::mem_fun( *this, &BrowserContextCommands::on_dialog_node_removed)); std::unique_ptr entry_dialog( EntryDialog::create( m_parent, directory ? _("Choose a name for the directory") : _("Choose a name for the document"), directory ? _("_Directory Name:") : _("_Document Name:"))); entry_dialog->add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL); entry_dialog->add_button(_("C_reate"), Gtk::RESPONSE_ACCEPT); entry_dialog->set_text(directory ? _("New Directory") : _("New Document")); entry_dialog->signal_response().connect(sigc::bind( sigc::mem_fun(*this, &BrowserContextCommands::on_new_response), browser, iter, directory)); m_dialog = std::move(entry_dialog); m_dialog->present(); } void Gobby::BrowserContextCommands::on_open(const Glib::VariantBase& param) { InfBrowser* browser = m_popup_watch->get_browser(); const InfBrowserIter iter = *m_popup_watch->get_browser_iter(); m_dialog_watch.reset(new NodeWatch(browser, &iter)); m_dialog_watch->signal_node_removed().connect(sigc::mem_fun( *this, &BrowserContextCommands::on_dialog_node_removed)); std::unique_ptr file_dialog( new FileChooser::Dialog( m_file_chooser, m_parent, _("Choose a text file to open"), Gtk::FILE_CHOOSER_ACTION_OPEN)); file_dialog->signal_response().connect(sigc::bind( sigc::mem_fun(*this, &BrowserContextCommands::on_open_response), browser, iter)); file_dialog->set_select_multiple(true); m_dialog = std::move(file_dialog); m_dialog->present(); } void Gobby::BrowserContextCommands::on_permissions( const Glib::VariantBase& param) { InfBrowser* browser = m_popup_watch->get_browser(); const InfBrowserIter iter = *m_popup_watch->get_browser_iter(); m_dialog_watch.reset(new NodeWatch(browser, &iter)); m_dialog_watch->signal_node_removed().connect(sigc::mem_fun( *this, &BrowserContextCommands::on_dialog_node_removed)); InfGtkPermissionsDialog* dlg = inf_gtk_permissions_dialog_new( m_parent.gobj(), static_cast(0), browser, &iter); std::unique_ptr permissions_dialog( Glib::wrap(GTK_DIALOG(dlg))); permissions_dialog->add_button(_("_Close"), Gtk::RESPONSE_CLOSE); permissions_dialog->signal_response().connect( sigc::mem_fun(*this, &BrowserContextCommands::on_permissions_response)); m_dialog = std::move(permissions_dialog); m_dialog->present(); } void Gobby::BrowserContextCommands::on_delete( const Glib::VariantBase& param) { InfBrowser* browser = m_popup_watch->get_browser(); const InfBrowserIter* iter = m_popup_watch->get_browser_iter(); m_operations.delete_node(browser, iter); } // Dialogs void Gobby::BrowserContextCommands::on_dialog_node_removed() { m_dialog.reset(NULL); m_dialog_watch.reset(NULL); } void Gobby::BrowserContextCommands::on_create_account_response( int response_id) { m_dialog.reset(NULL); m_dialog_watch.reset(NULL); } void Gobby::BrowserContextCommands::on_connection_info_response( int response_id) { m_dialog.reset(NULL); m_dialog_watch.reset(NULL); } void Gobby::BrowserContextCommands::on_account_created( gnutls_x509_privkey_t key, InfCertificateChain* certificate, const InfAclAccount* account) { InfBrowser* browser; g_object_get(G_OBJECT(m_dialog->gobj()), "browser", &browser, NULL); const InfAclAccount* own_account = inf_browser_get_acl_local_account(browser); const InfAclAccountId default_id = inf_acl_account_id_from_string("default"); const bool is_default_account = (own_account->id == default_id); g_object_unref(browser); gnutls_x509_crt_t cert = inf_certificate_chain_get_own_certificate(certificate); gchar* name = inf_cert_util_get_dn_by_oid( cert, GNUTLS_OID_X520_COMMON_NAME, 0); const std::string cn = name; g_free(name); std::string host; if(INFC_IS_BROWSER(browser)) { InfXmlConnection* xml = infc_browser_get_connection(INFC_BROWSER(browser)); if(INF_IS_XMPP_CONNECTION(xml)) { gchar* hostname; g_object_get(G_OBJECT(xml), "remote-hostname", &hostname, NULL); host = hostname; g_free(hostname); } } if(host.empty()) host = "local"; gnutls_x509_crt_t* certs = inf_certificate_chain_get_raw(certificate); const unsigned int n_certs = inf_certificate_chain_get_n_certificates(certificate); std::unique_ptr dlg; try { const std::string filename = make_certificate_filename(cn, host); GError* error = NULL; inf_cert_util_write_certificate_with_key( key, certs, n_certs, filename.c_str(), &error); if(error != NULL) { const std::string message = error->message; g_error_free(error); throw std::runtime_error(message); } if(is_default_account && (m_cert_manager.get_private_key() == NULL || m_cert_manager.get_certificates() == NULL)) { m_preferences.security.key_file = filename; m_preferences.security.certificate_file = filename; dlg.reset(new Gtk::MessageDialog( m_parent, _("Account successfully created"), false, Gtk::MESSAGE_INFO, Gtk::BUTTONS_CLOSE)); dlg->set_secondary_text( _("When re-connecting to the server, the " "new account will be used.")); } else { // TODO: Gobby should support multiple certificates dlg.reset(new Gtk::MessageDialog( m_parent, _("Account successfully created"), false, Gtk::MESSAGE_INFO, Gtk::BUTTONS_CLOSE)); dlg->set_secondary_text(Glib::ustring::compose( _("The certificate has been stored at %1.\n\n" "To login to this account, set the " "certificate in Gobby's preferences " "and re-connect to the server."), filename)); } } catch(const std::exception& ex) { // This is actually a bit unfortunate, because the // account was actually created and we have a valid // certificate for it, but we cannot keep it... dlg.reset(new Gtk::MessageDialog( m_parent, _("Failed to create account"), false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_CLOSE)); dlg->set_secondary_text(Glib::ustring::compose( _("Could not save the certificate for the " "account: %1"), ex.what())); } m_dialog = std::move(dlg); m_dialog->signal_response().connect( sigc::mem_fun( *this, &BrowserContextCommands:: on_account_created_response)); m_dialog->present(); } void Gobby::BrowserContextCommands::on_account_created_response( int response_id) { m_dialog.reset(NULL); } void Gobby::BrowserContextCommands::on_new_response(int response_id, InfBrowser* browser, InfBrowserIter iter, bool directory) { EntryDialog* entry_dialog = static_cast(m_dialog.get()); if(response_id == Gtk::RESPONSE_ACCEPT) { if(directory) { // TODO: Select the newly created directory in tree m_operations.create_directory( browser, &iter, entry_dialog->get_text()); } else { m_operations.create_document( browser, &iter, entry_dialog->get_text()); } } m_dialog.reset(NULL); m_dialog_watch.reset(NULL); } void Gobby::BrowserContextCommands::on_open_response(int response_id, InfBrowser* browser, InfBrowserIter iter) { FileChooser::Dialog* dialog = static_cast(m_dialog.get()); if(response_id == Gtk::RESPONSE_ACCEPT) { const std::vector > files = dialog->get_files(); m_operations.create_documents( browser, &iter, m_preferences, files); } m_dialog.reset(NULL); m_dialog_watch.reset(NULL); } void Gobby::BrowserContextCommands::on_permissions_response(int response_id) { m_dialog.reset(NULL); m_dialog_watch.reset(NULL); } code/commands/browser-context-commands.hpp000066400000000000000000000107711304552132100212500ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_BROWSER_CONTEXT_COMMANDS_HPP_ #define _GOBBY_BROWSER_CONTEXT_COMMANDS_HPP_ #include "operations/operations.hpp" #include "dialogs/entry-dialog.hpp" #include "core/nodewatch.hpp" #include "core/browser.hpp" #include "core/filechooser.hpp" #include #include #include #include namespace Gobby { class BrowserContextCommands: public sigc::trackable { public: BrowserContextCommands(Gtk::Window& parent, InfIo* io, Browser& browser, FileChooser& chooser, Operations& operations, const CertificateManager& cert_manager, Preferences& prf); ~BrowserContextCommands(); protected: static void on_populate_popup_static(InfGtkBrowserView* view, GtkMenu* menu, gpointer user_data) { static_cast(user_data)-> on_populate_popup(Glib::wrap(menu)); } static void on_account_created_static( InfGtkAccountCreationDialog* dlg, gnutls_x509_privkey_t key, InfCertificateChain* certificate, const InfAclAccount* account, gpointer user_data) { static_cast(user_data)-> on_account_created(key, certificate, account); } void on_menu_node_removed(); void on_menu_deactivate(); void on_populate_popup(Gtk::Menu* menu); void on_popdown(); // Context commands void on_remove(const Glib::VariantBase& param); void on_connection_info(const Glib::VariantBase& param); void on_disconnect(const Glib::VariantBase& param); void on_create_account(const Glib::VariantBase& param); void on_new(const Glib::VariantBase& param, bool directory); void on_open(const Glib::VariantBase& param); void on_permissions(const Glib::VariantBase& param); void on_delete(const Glib::VariantBase& param); void on_dialog_node_removed(); void on_create_account_response(int response_id); void on_account_created(gnutls_x509_privkey_t key, InfCertificateChain* certificate, const InfAclAccount* account); void on_connection_info_response(int response_id); void on_account_created_response(int response_id); void on_new_response(int response_id, InfBrowser* browser, InfBrowserIter iter, bool directory); void on_open_response(int response_id, InfBrowser* browser, InfBrowserIter iter); void on_permissions_response(int response_id); Gtk::Window& m_parent; InfIo* m_io; Browser& m_browser; FileChooser& m_file_chooser; Operations& m_operations; const CertificateManager& m_cert_manager; Preferences& m_preferences; // Popup menu Gtk::Menu* m_popup_menu; std::unique_ptr m_popup_watch; gulong m_populate_popup_handler; // Dialog std::unique_ptr m_dialog; std::unique_ptr m_dialog_watch; // Allowed Actions (TODO: move them to browsercontextactions.cpp) const Glib::RefPtr m_action_group; const Glib::RefPtr m_action_remove; const Glib::RefPtr m_action_connection_info; const Glib::RefPtr m_action_disconnect; const Glib::RefPtr m_action_create_account; const Glib::RefPtr m_action_create_document; const Glib::RefPtr m_action_create_directory; const Glib::RefPtr m_action_open_document; const Glib::RefPtr m_action_permissions; const Glib::RefPtr m_action_delete; }; } #endif // _GOBBY_BROWSER_CONTEXT_COMMANDS_HPP_ code/commands/edit-commands.cpp000066400000000000000000000341251304552132100170220ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "commands/edit-commands.hpp" #include "util/i18n.hpp" namespace { } // anonymous namespace Gobby::EditCommands::EditCommands(Gtk::Window& parent, WindowActions& actions, const Folder& folder, StatusBar& status_bar): m_parent(parent), m_actions(actions), m_folder(folder), m_status_bar(status_bar), m_current_view(NULL) { actions.undo->signal_activate().connect(sigc::hide( sigc::mem_fun(*this, &EditCommands::on_undo))); actions.redo->signal_activate().connect(sigc::hide( sigc::mem_fun(*this, &EditCommands::on_redo))); actions.cut->signal_activate().connect(sigc::hide( sigc::mem_fun(*this, &EditCommands::on_cut))); actions.copy->signal_activate().connect(sigc::hide( sigc::mem_fun(*this, &EditCommands::on_copy))); actions.paste->signal_activate().connect(sigc::hide( sigc::mem_fun(*this, &EditCommands::on_paste))); actions.find->signal_activate().connect(sigc::hide( sigc::mem_fun(*this, &EditCommands::on_find))); actions.find_next->signal_activate().connect(sigc::hide( sigc::mem_fun(*this, &EditCommands::on_find_next))); actions.find_prev->signal_activate().connect(sigc::hide( sigc::mem_fun(*this, &EditCommands::on_find_prev))); actions.find_replace->signal_activate().connect(sigc::hide( sigc::mem_fun(*this, &EditCommands::on_find_replace))); actions.goto_line->signal_activate().connect(sigc::hide( sigc::mem_fun(*this, &EditCommands::on_goto_line))); m_folder.signal_document_removed().connect( sigc::mem_fun(*this, &EditCommands::on_document_removed)); m_folder.signal_document_changed().connect( sigc::mem_fun(*this, &EditCommands::on_document_changed)); // Setup initial sensitivity: on_document_changed(m_folder.get_current_document()); } Gobby::EditCommands::~EditCommands() { // Disconnect handlers from current document: on_document_changed(NULL); } void Gobby::EditCommands::on_document_removed(SessionView& view) { // TODO: Isn't this emitted by Folder already? if(&view == m_current_view) on_document_changed(NULL); } void Gobby::EditCommands::on_document_changed(SessionView* view) { if(m_current_view != NULL) { InfTextSession* session = m_current_view->get_session(); InfAdoptedAlgorithm* algorithm = inf_adopted_session_get_algorithm( INF_ADOPTED_SESSION(session)); GtkTextBuffer* buffer = GTK_TEXT_BUFFER( m_current_view->get_text_buffer()); if(m_synchronization_complete_handler != 0) { g_signal_handler_disconnect( G_OBJECT(session), m_synchronization_complete_handler); } else { g_signal_handler_disconnect( G_OBJECT(algorithm), m_can_undo_changed_handler); g_signal_handler_disconnect( G_OBJECT(algorithm), m_can_redo_changed_handler); } g_signal_handler_disconnect(G_OBJECT(buffer), m_mark_set_handler); g_signal_handler_disconnect(G_OBJECT(buffer), m_changed_handler); m_active_user_changed_connection.disconnect(); } m_current_view = dynamic_cast(view); if(m_current_view != NULL) { InfTextSession* session = m_current_view->get_session(); InfUser* active_user = m_current_view->get_active_user(); GtkTextBuffer* buffer = GTK_TEXT_BUFFER(m_current_view->get_text_buffer()); m_active_user_changed_connection = m_current_view->signal_active_user_changed().connect( sigc::mem_fun( *this, &EditCommands:: on_active_user_changed)); m_mark_set_handler = g_signal_connect_after( G_OBJECT(buffer), "mark-set", G_CALLBACK(&on_mark_set_static), this); // The selection might change without mark-set being emitted // when the document changes, for example when all // currently selected text is deleted. m_changed_handler = g_signal_connect_after( G_OBJECT(buffer), "changed", G_CALLBACK(&on_changed_static), this); if(inf_session_get_status(INF_SESSION(session)) == INF_SESSION_RUNNING) { // This connects to can-undo-changed and // can-redo-changed of the algorithm. Set // m_synchronization_complete_handler to zero so that // the function does not try to disconnect from it. m_synchronization_complete_handler = 0; on_sync_complete(); } else { // The InfAdoptedSession is created after // synchronization, so we wait until that finished. m_synchronization_complete_handler = g_signal_connect_after( G_OBJECT(session), "synchronization_complete", G_CALLBACK(&on_sync_complete_static), this); m_can_undo_changed_handler = 0; m_can_redo_changed_handler = 0; } // Set initial sensitivity for active user: on_active_user_changed(active_user); // Set initial sensitivity for cut/copy/paste: on_mark_set(); // Set initial sensitivity for find/replace/goto: m_actions.find->set_enabled(true); if(m_find_dialog.get()) { on_find_text_changed(); } else { m_actions.find_next->set_enabled(false); m_actions.find_prev->set_enabled(false); } m_actions.find_replace->set_enabled(true); m_actions.goto_line->set_enabled(true); } else { m_actions.undo->set_enabled(false); m_actions.redo->set_enabled(false); m_actions.cut->set_enabled(false); m_actions.copy->set_enabled(false); m_actions.paste->set_enabled(false); m_actions.find->set_enabled(false); m_actions.find_next->set_enabled(false); m_actions.find_prev->set_enabled(false); m_actions.find_replace->set_enabled(false); m_actions.goto_line->set_enabled(false); } } void Gobby::EditCommands::on_sync_complete() { g_assert(m_current_view != NULL); InfTextSession* session = m_current_view->get_session(); InfAdoptedAlgorithm* algorithm = inf_adopted_session_get_algorithm( INF_ADOPTED_SESSION(session)); m_can_undo_changed_handler = g_signal_connect( G_OBJECT(algorithm), "can-undo-changed", G_CALLBACK(&on_can_undo_changed_static), this); m_can_redo_changed_handler = g_signal_connect( G_OBJECT(algorithm), "can-redo-changed", G_CALLBACK(&on_can_redo_changed_static), this); if(m_synchronization_complete_handler != 0) { g_signal_handler_disconnect( G_OBJECT(session), m_synchronization_complete_handler); m_synchronization_complete_handler = 0; } } void Gobby::EditCommands::on_active_user_changed(InfUser* active_user) { g_assert(m_current_view != NULL); if(active_user != NULL) { InfTextSession* session = m_current_view->get_session(); InfAdoptedAlgorithm* algorithm = inf_adopted_session_get_algorithm( INF_ADOPTED_SESSION(session)); GtkTextBuffer* buffer = GTK_TEXT_BUFFER( m_current_view->get_text_buffer()); m_actions.undo->set_enabled( inf_adopted_algorithm_can_undo( algorithm, INF_ADOPTED_USER(active_user))); m_actions.redo->set_enabled( inf_adopted_algorithm_can_redo( algorithm, INF_ADOPTED_USER(active_user))); m_actions.cut->set_enabled( gtk_text_buffer_get_has_selection(buffer)); m_actions.paste->set_enabled(true); } else { m_actions.undo->set_enabled(false); m_actions.redo->set_enabled(false); m_actions.cut->set_enabled(false); m_actions.paste->set_enabled(false); } } void Gobby::EditCommands::on_mark_set() { g_assert(m_current_view != NULL); GtkTextBuffer* buffer = GTK_TEXT_BUFFER(m_current_view->get_text_buffer()); m_actions.copy->set_enabled( gtk_text_buffer_get_has_selection(buffer)); if(m_current_view->get_active_user() != NULL) { m_actions.cut->set_enabled( gtk_text_buffer_get_has_selection(buffer)); } } void Gobby::EditCommands::on_changed() { on_mark_set(); } void Gobby::EditCommands::on_can_undo_changed(InfAdoptedUser* user, bool can_undo) { g_assert(m_current_view != NULL); if(INF_ADOPTED_USER(m_current_view->get_active_user()) == user) m_actions.undo->set_enabled(can_undo); } void Gobby::EditCommands::on_can_redo_changed(InfAdoptedUser* user, bool can_redo) { g_assert(m_current_view != NULL); if(INF_ADOPTED_USER(m_current_view->get_active_user()) == user) m_actions.redo->set_enabled(can_redo); } void Gobby::EditCommands::on_find_text_changed() { m_actions.find_next->set_enabled( !m_find_dialog->get_find_text().empty()); m_actions.find_prev->set_enabled( !m_find_dialog->get_find_text().empty()); } // TODO: The following is basically a hack to set the cursor to the position // where a Undo/Redo has happened. This can be properly fixed as soon as // libinfinity supports caret-aware requests, by generating undo-caret and // redo-caret requests. namespace { GtkTextMark* check = NULL; void recaret_i(GtkTextBuffer* buffer, GtkTextIter* location, gchar* text, gint len, gpointer user_data) { if(!check) { check = gtk_text_buffer_create_mark(buffer, NULL, location, FALSE); } else { GtkTextIter iter; gtk_text_buffer_get_iter_at_mark(buffer, &iter, check); if(gtk_text_iter_get_offset(&iter) < gtk_text_iter_get_offset(location)) gtk_text_buffer_move_mark(buffer, check, location); } } void recaret_e(GtkTextBuffer* buffer, GtkTextIter* start, GtkTextIter* end, gpointer user_data) { if(!check) { check = gtk_text_buffer_create_mark(buffer, NULL, start, FALSE); } else { GtkTextIter iter; gtk_text_buffer_get_iter_at_mark(buffer, &iter, check); if(gtk_text_iter_get_offset(&iter) < gtk_text_iter_get_offset(start)) gtk_text_buffer_move_mark(buffer, check, start); } } } void Gobby::EditCommands::on_undo() { g_assert(m_current_view != NULL); gulong i_ = g_signal_connect_after(m_current_view->get_text_buffer(), "insert-text", G_CALLBACK(recaret_i), NULL); gulong e_ = g_signal_connect_after(m_current_view->get_text_buffer(), "delete-range", G_CALLBACK(recaret_e), NULL); inf_adopted_session_undo( INF_ADOPTED_SESSION(m_current_view->get_session()), INF_ADOPTED_USER(m_current_view->get_active_user()), m_current_view->get_undo_grouping().get_undo_size() ); g_signal_handler_disconnect(m_current_view->get_text_buffer(), i_); g_signal_handler_disconnect(m_current_view->get_text_buffer(), e_); if(check) { GtkTextIter check_iter; gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(m_current_view->get_text_buffer()), &check_iter, check); gtk_text_buffer_select_range(GTK_TEXT_BUFFER(m_current_view->get_text_buffer()), &check_iter, &check_iter); gtk_text_buffer_delete_mark(GTK_TEXT_BUFFER(m_current_view->get_text_buffer()), check); check = NULL; } m_current_view->scroll_to_cursor_position(0.0); } void Gobby::EditCommands::on_redo() { g_assert(m_current_view != NULL); gulong i_ = g_signal_connect_after(m_current_view->get_text_buffer(), "insert-text", G_CALLBACK(recaret_i), NULL); gulong e_ = g_signal_connect_after(m_current_view->get_text_buffer(), "delete-range", G_CALLBACK(recaret_e), NULL); inf_adopted_session_redo( INF_ADOPTED_SESSION(m_current_view->get_session()), INF_ADOPTED_USER(m_current_view->get_active_user()), m_current_view->get_undo_grouping().get_redo_size() ); g_signal_handler_disconnect(m_current_view->get_text_buffer(), i_); g_signal_handler_disconnect(m_current_view->get_text_buffer(), e_); if(check) { GtkTextIter check_iter; gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(m_current_view->get_text_buffer()), &check_iter, check); gtk_text_buffer_select_range(GTK_TEXT_BUFFER(m_current_view->get_text_buffer()), &check_iter, &check_iter); gtk_text_buffer_delete_mark(GTK_TEXT_BUFFER(m_current_view->get_text_buffer()), check); check = NULL; } m_current_view->scroll_to_cursor_position(0.0); } void Gobby::EditCommands::on_cut() { g_assert(m_current_view != NULL); g_assert(m_current_view->get_active_user() != NULL); gtk_text_buffer_cut_clipboard( GTK_TEXT_BUFFER(m_current_view->get_text_buffer()), gtk_clipboard_get(GDK_SELECTION_CLIPBOARD), TRUE); m_current_view->scroll_to_cursor_position(0.0); } void Gobby::EditCommands::on_copy() { g_assert(m_current_view != NULL); gtk_text_buffer_copy_clipboard( GTK_TEXT_BUFFER(m_current_view->get_text_buffer()), gtk_clipboard_get(GDK_SELECTION_CLIPBOARD)); } void Gobby::EditCommands::on_paste() { g_assert(m_current_view != NULL); g_assert(m_current_view->get_active_user() != NULL); gtk_text_buffer_paste_clipboard( GTK_TEXT_BUFFER(m_current_view->get_text_buffer()), gtk_clipboard_get(GDK_SELECTION_CLIPBOARD), NULL, TRUE); m_current_view->scroll_to_cursor_position(0.0); } void Gobby::EditCommands::on_find() { ensure_find_dialog(); m_find_dialog->set_search_only(true); m_find_dialog->present(); } void Gobby::EditCommands::on_find_next() { g_assert(m_find_dialog.get() != NULL); m_find_dialog->find_next(); } void Gobby::EditCommands::on_find_prev() { g_assert(m_find_dialog.get() != NULL); m_find_dialog->find_previous(); } void Gobby::EditCommands::on_find_replace() { ensure_find_dialog(); m_find_dialog->set_search_only(false); m_find_dialog->present(); } void Gobby::EditCommands::on_goto_line() { if(!m_goto_dialog.get()) m_goto_dialog = GotoDialog::create(m_parent, m_folder); m_goto_dialog->present(); } void Gobby::EditCommands::ensure_find_dialog() { if(!m_find_dialog.get()) { m_find_dialog = FindDialog::create(m_parent, m_folder, m_status_bar); m_find_dialog->signal_find_text_changed().connect( sigc::mem_fun( *this, &EditCommands::on_find_text_changed)); } } code/commands/edit-commands.hpp000066400000000000000000000075431304552132100170330ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_EDIT_COMMANDS_HPP_ #define _GOBBY_EDIT_COMMANDS_HPP_ #include "dialogs/find-dialog.hpp" #include "dialogs/goto-dialog.hpp" #include "dialogs/preferences-dialog.hpp" #include "core/folder.hpp" #include "core/statusbar.hpp" #include "core/windowactions.hpp" #include #include #include namespace Gobby { class EditCommands: public sigc::trackable { public: EditCommands(Gtk::Window& parent, WindowActions& actions, const Folder& folder, StatusBar& status_bar); ~EditCommands(); protected: void on_document_removed(SessionView& view); void on_document_changed(SessionView* view); static void on_can_undo_changed_static(InfAdoptedAlgorithm* algorithm, InfAdoptedUser* user, gboolean can_undo, gpointer user_data) { static_cast(user_data)->on_can_undo_changed( user, can_undo); } static void on_can_redo_changed_static(InfAdoptedAlgorithm* algorithm, InfAdoptedUser* user, gboolean can_redo, gpointer user_data) { static_cast(user_data)->on_can_redo_changed( user, can_redo); } static void on_sync_complete_static(InfSession* session, InfXmlConnection* connection, gpointer user_data) { static_cast(user_data)->on_sync_complete(); } static void on_mark_set_static(GtkTextBuffer* buffer, GtkTextIter* iter, GtkTextMark* mark, gpointer user_data) { static_cast(user_data)->on_mark_set(); } static void on_changed_static(GtkTextBuffer* buffer, gpointer user_data) { static_cast(user_data)->on_changed(); } void on_sync_complete(); void on_active_user_changed(InfUser* active_user); void on_mark_set(); void on_changed(); void on_can_undo_changed(InfAdoptedUser* user, bool can_undo); void on_can_redo_changed(InfAdoptedUser* user, bool can_redo); void on_find_text_changed(); void on_undo(); void on_redo(); void on_cut(); void on_copy(); void on_paste(); void on_find(); void on_find_next(); void on_find_prev(); void on_find_replace(); void on_goto_line(); Gtk::Window& m_parent; WindowActions& m_actions; const Folder& m_folder; StatusBar& m_status_bar; std::unique_ptr m_find_dialog; std::unique_ptr m_goto_dialog; TextSessionView* m_current_view; // Only valid when m_current_document is nonzero: sigc::connection m_active_user_changed_connection; gulong m_can_undo_changed_handler; gulong m_can_redo_changed_handler; gulong m_synchronization_complete_handler; gulong m_mark_set_handler; gulong m_changed_handler; private: void ensure_find_dialog(); }; } #endif // _GOBBY_EDIT_COMMANDS_HPP_ code/commands/file-commands.cpp000066400000000000000000000213221304552132100170070ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "commands/file-commands.hpp" #include "commands/file-tasks/task-new.hpp" #include "commands/file-tasks/task-open-file.hpp" #include "commands/file-tasks/task-open-location.hpp" #include "commands/file-tasks/task-save.hpp" #include "commands/file-tasks/task-save-all.hpp" #include "commands/file-tasks/task-export-html.hpp" #include "util/i18n.hpp" Gobby::FileCommands::Task::Task(FileCommands& file_commands): m_file_commands(file_commands) { } Gobby::FileCommands::Task::~Task() { } void Gobby::FileCommands::Task::finish() { // Note this could delete this: m_signal_finished.emit(); } Gtk::Window& Gobby::FileCommands::Task::get_parent() { return m_file_commands.m_parent; } const Gobby::Folder& Gobby::FileCommands::Task::get_folder() { return m_file_commands.m_folder_manager.get_text_folder(); } Gobby::StatusBar& Gobby::FileCommands::Task::get_status_bar() { return m_file_commands.m_status_bar; } Gobby::FileChooser& Gobby::FileCommands::Task::get_file_chooser() { return m_file_commands.m_file_chooser; } Gobby::Operations& Gobby::FileCommands::Task::get_operations() { return m_file_commands.m_operations; } const Gobby::DocumentInfoStorage& Gobby::FileCommands::Task::get_document_info_storage() { return m_file_commands.m_document_info_storage; } Gobby::Preferences& Gobby::FileCommands::Task::get_preferences() { return m_file_commands.m_preferences; } Gobby::DocumentLocationDialog& Gobby::FileCommands::Task::get_document_location_dialog() { if(m_file_commands.m_location_dialog.get() == NULL) { m_file_commands.m_location_dialog = DocumentLocationDialog::create( m_file_commands.m_parent, INF_GTK_BROWSER_MODEL( m_file_commands.m_browser. get_store())); } return *m_file_commands.m_location_dialog; } Gobby::FileCommands::FileCommands(Gtk::Window& parent, WindowActions& actions, Browser& browser, FolderManager& folder_manager, StatusBar& status_bar, FileChooser& file_chooser, Operations& operations, const DocumentInfoStorage& info_storage, Preferences& preferences): m_parent(parent), m_actions(actions), m_browser(browser), m_folder_manager(folder_manager), m_status_bar(status_bar), m_file_chooser(file_chooser), m_operations(operations), m_document_info_storage(info_storage), m_preferences(preferences) { actions.new_document->signal_activate().connect(sigc::hide( sigc::mem_fun(*this, &FileCommands::on_new))); actions.open->signal_activate().connect(sigc::hide( sigc::mem_fun(*this, &FileCommands::on_open))); actions.open_location->signal_activate().connect(sigc::hide( sigc::mem_fun(*this, &FileCommands::on_open_location))); actions.save->signal_activate().connect(sigc::hide( sigc::mem_fun(*this, &FileCommands::on_save))); actions.save_as->signal_activate().connect(sigc::hide( sigc::mem_fun(*this, &FileCommands::on_save_as))); actions.save_all->signal_activate().connect(sigc::hide( sigc::mem_fun(*this, &FileCommands::on_save_all))); actions.export_html->signal_activate().connect(sigc::hide( sigc::mem_fun(*this, &FileCommands::on_export_html))); actions.connect->signal_activate().connect(sigc::hide( sigc::mem_fun(*this, &FileCommands::on_connect))); actions.close->signal_activate().connect(sigc::hide( sigc::mem_fun(*this, &FileCommands::on_close))); folder_manager.get_text_folder().signal_document_changed().connect( sigc::mem_fun(*this, &FileCommands::on_document_changed)); InfGtkBrowserModelSort* store = browser.get_store(); m_row_inserted_handler = g_signal_connect(G_OBJECT(store), "row-inserted", G_CALLBACK(on_row_inserted_static), this); m_row_deleted_handler = g_signal_connect(G_OBJECT(store), "row-deleted", G_CALLBACK(on_row_deleted_static), this); update_sensitivity(); } Gobby::FileCommands::~FileCommands() { InfGtkBrowserModelSort* store = m_browser.get_store(); g_signal_handler_disconnect(G_OBJECT(store), m_row_inserted_handler); g_signal_handler_disconnect(G_OBJECT(store), m_row_deleted_handler); } void Gobby::FileCommands::set_task(Task* task) { task->signal_finished().connect(sigc::mem_fun( *this, &FileCommands::on_task_finished)); m_task.reset(task); task->run(); } void Gobby::FileCommands::on_document_changed(SessionView* view) { update_sensitivity(); } void Gobby::FileCommands::on_row_inserted() { update_sensitivity(); } void Gobby::FileCommands::on_row_deleted() { update_sensitivity(); } void Gobby::FileCommands::on_task_finished() { m_task.reset(NULL); } void Gobby::FileCommands::on_new() { set_task(new TaskNew(*this)); } void Gobby::FileCommands::on_open() { set_task(new TaskOpenFile(*this)); } void Gobby::FileCommands::on_open_location() { set_task(new TaskOpenLocation(*this)); } void Gobby::FileCommands::on_save() { SessionView* view = m_folder_manager.get_text_folder().get_current_document(); TextSessionView* text_view = dynamic_cast(view); g_assert(text_view != NULL); const DocumentInfoStorage::Info* info = m_document_info_storage.get_info( text_view->get_info_storage_key()); if(info != NULL && !info->uri.empty()) { Glib::RefPtr file = Gio::File::create_for_uri(info->uri); m_operations.save_document( *text_view, file, info->encoding, info->eol_style); } else { on_save_as(); } } void Gobby::FileCommands::on_save_as() { SessionView* view = m_folder_manager.get_text_folder().get_current_document(); TextSessionView* text_view = dynamic_cast(view); g_assert(text_view != NULL); set_task(new TaskSave(*this, *text_view)); } void Gobby::FileCommands::on_save_all() { set_task(new TaskSaveAll(*this)); } void Gobby::FileCommands::on_export_html() { SessionView* view = m_folder_manager.get_text_folder().get_current_document(); TextSessionView* text_view = dynamic_cast(view); g_assert(text_view != NULL); set_task(new TaskExportHtml(*this, *text_view)); } void Gobby::FileCommands::on_connect() { if(m_connection_dialog.get() == NULL) { m_connection_dialog = ConnectionDialog::create(m_parent); m_connection_dialog->signal_response().connect( sigc::mem_fun(*this, &FileCommands::on_connect_response)); m_connection_dialog->add_button(_("Cancel"), Gtk::RESPONSE_CANCEL); m_connection_dialog->add_button(_("Connect"), Gtk::RESPONSE_ACCEPT); } m_connection_dialog->present(); } void Gobby::FileCommands::on_close() { SessionView* view = m_folder_manager.get_text_folder().get_current_document(); g_assert(view != NULL); m_folder_manager.remove_document(*view); } void Gobby::FileCommands::on_connect_response(int response_id) { if(response_id == Gtk::RESPONSE_ACCEPT) { const Glib::ustring& host = m_connection_dialog->get_host_name(); if(!host.empty()) { m_operations.subscribe_path(host); } } m_connection_dialog->hide(); } void Gobby::FileCommands::update_sensitivity() { GtkTreeIter dummy_iter; const bool create_sensitivity = gtk_tree_model_get_iter_first( GTK_TREE_MODEL(m_browser.get_store()), &dummy_iter); SessionView* view = m_folder_manager.get_text_folder().get_current_document(); const bool view_sensitivity = view != NULL; // We can only save text documents currently const bool text_sensitivity = dynamic_cast(view) != NULL; m_actions.new_document->set_enabled(create_sensitivity); m_actions.open->set_enabled(create_sensitivity); m_actions.open_location->set_enabled(create_sensitivity); m_actions.save->set_enabled(text_sensitivity); m_actions.save_as->set_enabled(text_sensitivity); m_actions.save_all->set_enabled(text_sensitivity); m_actions.export_html->set_enabled(text_sensitivity); m_actions.close->set_enabled(view_sensitivity); } code/commands/file-commands.hpp000066400000000000000000000075311304552132100170220ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ // TODO: Someone else should do task management #ifndef _GOBBY_FILE_COMMANDS_HPP_ #define _GOBBY_FILE_COMMANDS_HPP_ #include "operations/operations.hpp" #include "dialogs/document-location-dialog.hpp" #include "dialogs/connection-dialog.hpp" #include "core/browser.hpp" #include "core/statusbar.hpp" #include "core/filechooser.hpp" #include "core/windowactions.hpp" #include #include namespace Gobby { class FileCommands: public sigc::trackable { public: FileCommands(Gtk::Window& parent, WindowActions& actions, Browser& browser, FolderManager& folder_manager, StatusBar& status_bar, FileChooser& file_chooser, Operations& operations, const DocumentInfoStorage& info_storage, Preferences& preferences); ~FileCommands(); class Task: public sigc::trackable { public: typedef sigc::signal SignalFinished; Task(FileCommands& file_commands); virtual ~Task() = 0; virtual void run() = 0; void finish(); Gtk::Window& get_parent(); const Folder& get_folder(); StatusBar& get_status_bar(); FileChooser& get_file_chooser(); Operations& get_operations(); const DocumentInfoStorage& get_document_info_storage(); Preferences& get_preferences(); DocumentLocationDialog& get_document_location_dialog(); SignalFinished signal_finished() const { return m_signal_finished; } protected: FileCommands& m_file_commands; SignalFinished m_signal_finished; }; void set_task(Task* task); protected: static void on_row_inserted_static(GtkTreeModel* model, GtkTreePath* path, GtkTreeIter* iter, gpointer user_data) { static_cast(user_data)->on_row_inserted(); } static void on_row_deleted_static(GtkTreeModel* model, GtkTreePath* path, gpointer user_data) { static_cast(user_data)->on_row_deleted(); } void on_document_changed(SessionView* view); void on_row_inserted(); void on_row_deleted(); void on_task_finished(); void on_new(); void on_open(); void on_open_location(); void on_save(); void on_save_as(); void on_save_all(); void on_export_html(); void on_connect(); void on_close(); void on_connect_response(int response_id); void update_sensitivity(); Gtk::Window& m_parent; WindowActions& m_actions; Browser& m_browser; FolderManager& m_folder_manager; StatusBar& m_status_bar; FileChooser& m_file_chooser; Operations& m_operations; const DocumentInfoStorage& m_document_info_storage; Preferences& m_preferences; // Note: Order is important to get deinitialization right: Task may // access the location dialog in its destructor, so make sure the task // is freed before the location dialog. std::unique_ptr m_location_dialog; std::unique_ptr m_task; std::unique_ptr m_connection_dialog; gulong m_row_inserted_handler; gulong m_row_deleted_handler; }; } #endif // _GOBBY_FILE_COMMANDS_HPP_ code/commands/file-tasks/000077500000000000000000000000001304552132100156275ustar00rootroot00000000000000code/commands/file-tasks/Makefile.am000066400000000000000000000014121304552132100176610ustar00rootroot00000000000000gobby_0_5_SOURCES += \ code/commands/file-tasks/task-export-html.cpp \ code/commands/file-tasks/task-new.cpp \ code/commands/file-tasks/task-open.cpp \ code/commands/file-tasks/task-open-file.cpp \ code/commands/file-tasks/task-open-location.cpp \ code/commands/file-tasks/task-open-multiple.cpp \ code/commands/file-tasks/task-save.cpp \ code/commands/file-tasks/task-save-all.cpp noinst_HEADERS += \ code/commands/file-tasks/task-export-html.hpp \ code/commands/file-tasks/task-new.hpp \ code/commands/file-tasks/task-open.hpp \ code/commands/file-tasks/task-open-file.hpp \ code/commands/file-tasks/task-open-location.hpp \ code/commands/file-tasks/task-open-multiple.hpp \ code/commands/file-tasks/task-save.hpp \ code/commands/file-tasks/task-save-all.hpp code/commands/file-tasks/task-export-html.cpp000066400000000000000000000043111304552132100215550ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ // TODO: Merge with TaskSave, or share code in a common base class #include "commands/file-tasks/task-export-html.hpp" #include "util/i18n.hpp" Gobby::TaskExportHtml::TaskExportHtml(FileCommands& file_commands, TextSessionView& view): Task(file_commands), m_file_dialog(get_file_chooser(), get_parent(), Glib::ustring::compose( _("Choose a location to export document \"%1\" to"), view.get_title()), Gtk::FILE_CHOOSER_ACTION_SAVE), m_view(&view), m_running(false) { get_folder().signal_document_removed().connect( sigc::mem_fun(*this, &TaskExportHtml::on_document_removed)); } void Gobby::TaskExportHtml::run() { // m_document will be set to NULL if it has been removed before run // was called. if(!m_view) { finish(); return; } m_running = true; m_file_dialog.signal_response().connect(sigc::mem_fun( *this, &TaskExportHtml::on_response)); m_file_dialog.set_current_name(m_view->get_title() + ".xhtml"); m_file_dialog.present(); } void Gobby::TaskExportHtml::on_response(int response_id) { if(response_id == Gtk::RESPONSE_ACCEPT) { get_operations().export_html( *m_view, m_file_dialog.get_file()); } finish(); } void Gobby::TaskExportHtml::on_document_removed(SessionView& view) { // The document we are about to save was removed. if(m_view == &view) { if(m_running) finish(); else m_view = NULL; } } code/commands/file-tasks/task-export-html.hpp000066400000000000000000000025421304552132100215660ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_FILE_TASK_EXPORT_HTML_HPP_ #define _GOBBY_FILE_TASK_EXPORT_HTML_HPP_ #include "commands/file-commands.hpp" namespace Gobby { class TaskExportHtml: public FileCommands::Task { private: FileChooser::Dialog m_file_dialog; TextSessionView* m_view; bool m_running; public: TaskExportHtml(FileCommands& file_commands, TextSessionView& view); virtual void run(); void on_response(int response_id); void on_document_removed(SessionView& view); }; } // namespace Gobby #endif // _GOBBY_FILE_TASK_EXPORT_HTML_HPP_ code/commands/file-tasks/task-new.cpp000066400000000000000000000033471304552132100200730ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "commands/file-tasks/task-new.hpp" #include "util/i18n.hpp" Gobby::TaskNew::TaskNew(FileCommands& file_commands): Task(file_commands) { } Gobby::TaskNew::~TaskNew() { get_document_location_dialog().hide(); } void Gobby::TaskNew::run() { DocumentLocationDialog& dialog = get_document_location_dialog(); dialog.signal_response().connect( sigc::mem_fun(*this, &TaskNew::on_response)); dialog.set_document_name(_("New Document")); dialog.set_single_document_mode(); dialog.present(); } void Gobby::TaskNew::on_response(int response_id) { if(response_id == Gtk::RESPONSE_ACCEPT) { DocumentLocationDialog& dialog = get_document_location_dialog(); InfBrowserIter iter; InfBrowser* browser = dialog.get_selected_directory(&iter); g_assert(browser != NULL); get_operations().create_document(browser, &iter, dialog.get_document_name()); } finish(); } code/commands/file-tasks/task-new.hpp000066400000000000000000000022761304552132100201000ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_FILE_TASK_NEW_HPP_ #define _GOBBY_FILE_TASK_NEW_HPP_ #include "commands/file-commands.hpp" namespace Gobby { class TaskNew: public FileCommands::Task { public: TaskNew(FileCommands& file_commands); virtual ~TaskNew(); virtual void run(); protected: void on_response(int response_id); }; } // namespcae Gobby #endif // _GOBBY_FILE_TASK_NEW_HPP_ code/commands/file-tasks/task-open-file.cpp000066400000000000000000000040151304552132100211510ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "commands/file-tasks/task-open-file.hpp" #include "util/i18n.hpp" Gobby::TaskOpenFile::TaskOpenFile(FileCommands& file_commands): Task(file_commands), m_file_dialog(get_file_chooser(), get_parent(), _("Choose a text file to open"), Gtk::FILE_CHOOSER_ACTION_OPEN) { m_file_dialog.set_select_multiple(true); } void Gobby::TaskOpenFile::run() { m_file_dialog.signal_response().connect(sigc::mem_fun( *this, &TaskOpenFile::on_file_response)); m_file_dialog.present(); } void Gobby::TaskOpenFile::on_file_response(int response_id) { if(response_id == Gtk::RESPONSE_ACCEPT) { m_file_dialog.hide(); std::vector > files = m_file_dialog.get_files(); g_assert(!files.empty()); if(files.size() == 1) { m_open_task.reset( new TaskOpen(m_file_commands, files[0])); m_open_task->signal_finished().connect( sigc::mem_fun(*this, &TaskOpenFile::finish)); m_open_task->run(); } else { TaskOpenMultiple *task = new TaskOpenMultiple(m_file_commands, files); m_open_taskm.reset(task); m_open_taskm->signal_finished().connect( sigc::mem_fun(*this, &TaskOpenFile::finish)); m_open_taskm->run(); } } else { finish(); } } code/commands/file-tasks/task-open-file.hpp000066400000000000000000000026521304552132100211630ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_FILE_TASK_OPEN_FILE_HPP_ #define _GOBBY_FILE_TASK_OPEN_FILE_HPP_ #include "commands/file-commands.hpp" #include "commands/file-tasks/task-open.hpp" #include "commands/file-tasks/task-open-multiple.hpp" namespace Gobby { class TaskOpenFile: public FileCommands::Task { public: TaskOpenFile(FileCommands& file_commands); virtual void run(); private: void on_file_response(int response_id); FileChooser::Dialog m_file_dialog; std::unique_ptr m_open_task; std::unique_ptr m_open_taskm; }; } // namespace Gobby #endif // _GOBBY_FILE_TASK_OPEN_FILE_HPP_ code/commands/file-tasks/task-open-location.cpp000066400000000000000000000035201304552132100220420ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "commands/file-tasks/task-open-location.hpp" #include "util/i18n.hpp" Gobby::TaskOpenLocation::TaskOpenLocation(FileCommands& file_commands): Task(file_commands) { } void Gobby::TaskOpenLocation::run() { m_location_dialog = OpenLocationDialog::create(get_parent()); m_location_dialog->signal_response().connect( sigc::mem_fun( *this, &TaskOpenLocation::on_response)); m_location_dialog->add_button(_("_Close"), Gtk::RESPONSE_CLOSE); m_location_dialog->add_button(_("_Open"), Gtk::RESPONSE_ACCEPT); m_location_dialog->present(); } void Gobby::TaskOpenLocation::on_response(int response_id) { if(response_id == Gtk::RESPONSE_ACCEPT) { std::string uri = m_location_dialog->get_uri(); Glib::RefPtr file = Gio::File::create_for_uri(uri); m_location_dialog.reset(NULL); m_open_task.reset(new TaskOpen(m_file_commands, file)); m_open_task->signal_finished().connect( sigc::mem_fun(*this, &TaskOpenLocation::finish)); m_open_task->run(); } else { finish(); // deletes this } } code/commands/file-tasks/task-open-location.hpp000066400000000000000000000026221304552132100220510ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_FILE_TASK_OPEN_LOCATION_HPP_ #define _GOBBY_FILE_TASK_OPEN_LOCATION_HPP_ #include "commands/file-commands.hpp" #include "commands/file-tasks/task-open.hpp" #include "dialogs/open-location-dialog.hpp" namespace Gobby { class TaskOpenLocation: public FileCommands::Task { public: TaskOpenLocation(FileCommands& file_commands); virtual void run(); private: void on_response(int response_id); std::unique_ptr m_location_dialog; std::unique_ptr m_open_task; }; } // namespace Gobby #endif // _GOBBY_FILE_TASK_OPEN_LOCATION_HPP_ code/commands/file-tasks/task-open-multiple.cpp000066400000000000000000000036131304552132100220700ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "commands/file-tasks/task-open-multiple.hpp" #include "operations/operation-open-multiple.hpp" Gobby::TaskOpenMultiple::TaskOpenMultiple(FileCommands& file_commands, const file_list& files): Task(file_commands), m_files(files) { } Gobby::TaskOpenMultiple::~TaskOpenMultiple() { get_document_location_dialog().hide(); } void Gobby::TaskOpenMultiple::run() { DocumentLocationDialog& dialog = get_document_location_dialog(); dialog.signal_response().connect(sigc::mem_fun( *this, &TaskOpenMultiple::on_location_response)); dialog.set_multiple_document_mode(); dialog.present(); } void Gobby::TaskOpenMultiple::on_location_response(int response_id) { if(response_id == Gtk::RESPONSE_ACCEPT) { DocumentLocationDialog& dialog = get_document_location_dialog(); InfBrowserIter iter; InfBrowser* browser = dialog.get_selected_directory(&iter); g_assert(browser != NULL); OperationOpenMultiple* operation = get_operations().create_documents( browser, &iter, get_preferences(), m_files); } finish(); } code/commands/file-tasks/task-open-multiple.hpp000066400000000000000000000027061304552132100220770ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_FILE_TASK_OPEN_MULTIPLE_HPP_ #define _GOBBY_FILE_TASK_OPEN_MULTIPLE_HPP_ #include "commands/file-commands.hpp" #include "operations/operation-open-multiple.hpp" #include #include namespace Gobby { class TaskOpenMultiple: public FileCommands::Task { public: typedef OperationOpenMultiple::file_list file_list; TaskOpenMultiple(FileCommands& file_commands, const file_list& files); virtual ~TaskOpenMultiple(); virtual void run(); private: void on_location_response(int response_id); const file_list m_files; }; } // namespace Gobby #endif // _GOBBY_FILE_TASK_OPEN_MULTIPLE_HPP_ code/commands/file-tasks/task-open.cpp000066400000000000000000000055671304552132100202510ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "commands/file-tasks/task-open.hpp" #include "util/i18n.hpp" Gobby::TaskOpen::TaskOpen(FileCommands& file_commands, const Glib::RefPtr& file): Task(file_commands), m_file(file) { } Gobby::TaskOpen::~TaskOpen() { if(m_handle != get_status_bar().invalid_handle()) get_status_bar().remove_message(m_handle); get_document_location_dialog().hide(); } void Gobby::TaskOpen::run() { try { // TODO: Show DocumentLocationDialog with a // default name as long as the query is // running. m_file->query_info_async( sigc::mem_fun(*this, &TaskOpen::on_query_info), G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME); m_handle = get_status_bar().add_info_message( Glib::ustring::compose( _("Querying \"%1\"..."), m_file->get_uri())); } catch(const Gio::Error& ex) { error(ex.what()); } } void Gobby::TaskOpen::on_query_info( const Glib::RefPtr& result) { get_status_bar().remove_message(m_handle); m_handle = get_status_bar().invalid_handle(); DocumentLocationDialog& dialog = get_document_location_dialog(); dialog.signal_response().connect(sigc::mem_fun( *this, &TaskOpen::on_location_response)); try { Glib::RefPtr info = m_file->query_info_finish(result); dialog.set_document_name(info->get_display_name()); dialog.set_single_document_mode(); dialog.present(); } catch(const Gio::Error& ex) { error(ex.what()); } } void Gobby::TaskOpen::on_location_response(int response_id) { if(response_id == Gtk::RESPONSE_ACCEPT) { DocumentLocationDialog& dialog = get_document_location_dialog(); InfBrowserIter iter; InfBrowser* browser = dialog.get_selected_directory(&iter); g_assert(browser != NULL); get_operations().create_document( browser, &iter, dialog.get_document_name(), get_preferences(), m_file, NULL); } finish(); } void Gobby::TaskOpen::error(const Glib::ustring& message) { get_status_bar().add_error_message( Glib::ustring::compose( _("Failed to open document \"%1\""), m_file->get_uri()), message); finish(); } code/commands/file-tasks/task-open.hpp000066400000000000000000000027101304552132100202410ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_FILE_TASK_OPEN_HPP_ #define _GOBBY_FILE_TASK_OPEN_HPP_ #include "commands/file-commands.hpp" #include namespace Gobby { class TaskOpen: public FileCommands::Task { public: TaskOpen(FileCommands& file_commands, const Glib::RefPtr& file); virtual ~TaskOpen(); virtual void run(); private: void on_query_info(const Glib::RefPtr& result); void on_location_response(int response_id); void error(const Glib::ustring& message); StatusBar::MessageHandle m_handle; Glib::RefPtr m_file; }; } // namespace Gobby #endif // _GOBBY_FILE_TASK_OPEN_HPP_ code/commands/file-tasks/task-save-all.cpp000066400000000000000000000054111304552132100210000ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ // TODO: This should not be a task because the asynchronous IO operations // should not be interrupted by tasks like "save as". // The should-not-abort part should be an operation. #include "commands/file-tasks/task-save-all.hpp" Gobby::TaskSaveAll::TaskSaveAll(FileCommands& file_commands): Task(file_commands) { } void Gobby::TaskSaveAll::run() { const unsigned int n_pages = get_folder().get_n_pages(); for(unsigned int i = 0; i < n_pages; ++i) { SessionView& view = get_folder().get_document(i); TextSessionView* text_view = dynamic_cast(&view); if(text_view) m_views.push_back(text_view); } get_folder().signal_document_removed().connect( sigc::mem_fun(*this, &TaskSaveAll::on_document_removed)); m_current = m_views.begin(); process_current(); } void Gobby::TaskSaveAll::on_document_removed(SessionView& view) { std::list::iterator iter = std::find( m_views.begin(), m_views.end(), &view); if(iter == m_current) { m_current = m_views.erase(m_current); // Go on with next process_current(); } if(iter != m_views.end()) m_views.erase(iter); } void Gobby::TaskSaveAll::on_finished() { m_current = m_views.erase(m_current); process_current(); } void Gobby::TaskSaveAll::process_current() { m_task.reset(NULL); if(m_current == m_views.end()) { finish(); } else { TextSessionView& view = **m_current; const DocumentInfoStorage::Info* info = get_document_info_storage().get_info( view.get_info_storage_key()); if(info != NULL && !info->uri.empty()) { Glib::RefPtr file = Gio::File::create_for_uri(info->uri); get_operations().save_document( view, file, info->encoding, info->eol_style); m_current = m_views.erase(m_current); process_current(); } else { m_task.reset(new TaskSave(m_file_commands, view)); m_task->signal_finished().connect(sigc::mem_fun( *this, &TaskSaveAll::on_finished)); m_task->run(); } } } code/commands/file-tasks/task-save-all.hpp000066400000000000000000000026501304552132100210070ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_FILE_TASK_SAVE_ALL_HPP_ #define _GOBBY_FILE_TASK_SAVE_ALL_HPP_ #include "commands/file-commands.hpp" #include "commands/file-tasks/task-save.hpp" namespace Gobby { class TaskSaveAll: public Gobby::FileCommands::Task { public: TaskSaveAll(FileCommands& file_commands); virtual void run(); private: void on_document_removed(SessionView& view); void on_finished(); void process_current(); std::list m_views; std::list::iterator m_current; std::unique_ptr m_task; }; } // namespace Gobby #endif // _GOBBY_FILE_TASK_SAVE_ALL_HPP_ code/commands/file-tasks/task-save.cpp000066400000000000000000000051161304552132100202340ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "commands/file-tasks/task-save.hpp" #include "util/i18n.hpp" Gobby::TaskSave::TaskSave(FileCommands& file_commands, TextSessionView& view): Task(file_commands), m_file_dialog(get_file_chooser(), get_parent(), Glib::ustring::compose( _("Choose a location to save document \"%1\" to"), view.get_title()), Gtk::FILE_CHOOSER_ACTION_SAVE), m_view(&view), m_running(false) { get_folder().signal_document_removed().connect( sigc::mem_fun( *this, &TaskSave::on_document_removed)); } void Gobby::TaskSave::run() { // m_view will be set to NULL if it has been removed before run // was called. if(!m_view) { finish(); return; } m_running = true; m_file_dialog.signal_response().connect(sigc::mem_fun( *this, &TaskSave::on_response)); const DocumentInfoStorage::Info* info = get_document_info_storage().get_info( m_view->get_info_storage_key()); if(info != NULL && !info->uri.empty()) m_file_dialog.set_uri(info->uri); else m_file_dialog.set_current_name(m_view->get_title()); m_file_dialog.present(); } void Gobby::TaskSave::on_response(int response_id) { if(response_id == Gtk::RESPONSE_ACCEPT) { const std::string& info_storage_key = m_view->get_info_storage_key(); const DocumentInfoStorage::Info* info = get_document_info_storage().get_info( info_storage_key); // TODO: Get encoding from file dialog // TODO: Default to CRLF on Windows get_operations().save_document( *m_view, m_file_dialog.get_file(), info ? info->encoding : "UTF-8", info ? info->eol_style : DocumentInfoStorage::EOL_LF); } finish(); } void Gobby::TaskSave::on_document_removed(SessionView& view) { // The document we are about to save was removed. if(m_view == &view) { if(m_running) finish(); else m_view = NULL; } } code/commands/file-tasks/task-save.hpp000066400000000000000000000025011304552132100202340ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_FILE_TASK_SAVE_HPP_ #define _GOBBY_FILE_TASK_SAVE_HPP_ #include "commands/file-commands.hpp" namespace Gobby { class TaskSave: public FileCommands::Task { private: FileChooser::Dialog m_file_dialog; TextSessionView* m_view; bool m_running; public: TaskSave(FileCommands& file_commands, TextSessionView& view); virtual void run(); void on_response(int response_id); void on_document_removed(SessionView& view); }; } // namespace Gobby #endif // _GOBBY_FILE_TASK_SAVE_HPP_ code/commands/folder-commands.cpp000066400000000000000000000205061304552132100173460ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "commands/folder-commands.hpp" #include "core/sessionuserview.hpp" #include #include // TODO: Make this work with non-text documents also... maybe split & // share a base class... or better: Move active user stuff to SessionView // base. All SessionView's should be modifyable by the user, and if there are // document types gobby only supports in a read-only kind of way the active // user can just be NULL all the time. class Gobby::FolderCommands::DocInfo: public sigc::trackable { public: static const unsigned int ACTIVATION_DELAY = 1000; DocInfo(SessionView& view): m_view(view), m_active_user(NULL), m_active(false) { m_view.signal_active_user_changed().connect( sigc::mem_fun( *this, &DocInfo::on_active_user_changed)); on_active_user_changed(view.get_active_user()); } virtual ~DocInfo() { if(m_active_user != NULL) { g_signal_handler_disconnect(G_OBJECT(m_active_user), m_notify_status_handle); } } virtual void deactivate() { m_active = false; if(m_active_user) deactivate_user(); } virtual void activate() { m_active = true; if(m_active_user) activate_user(); } virtual void flush() {} protected: void activate_user() { g_assert(!m_timeout_connection.connected()); g_assert(m_active_user != NULL); g_assert(inf_user_get_status(m_active_user) == INF_USER_INACTIVE); m_timeout_connection = Glib::signal_timeout().connect( sigc::mem_fun(*this, &DocInfo::on_activation_timeout), ACTIVATION_DELAY); } void deactivate_user() { g_assert(m_active_user != NULL); switch(inf_user_get_status(m_active_user)) { case INF_USER_INACTIVE: g_assert(m_timeout_connection.connected()); m_timeout_connection.disconnect(); break; case INF_USER_ACTIVE: /* Flush pending requests, so user is not set active * again later. TODO: Maybe this should become a * virtual function in InfSession actually. */ flush(); inf_session_set_user_status( INF_SESSION(m_view.get_session()), m_active_user, INF_USER_INACTIVE); break; case INF_USER_UNAVAILABLE: // It can happen that the user is already unavailable // here, for example when we have lost the connection // to the server, so this is not an error. // If the user was active before we lost the // connection then cancel the activation timeout if(m_timeout_connection.connected()) m_timeout_connection.disconnect(); // TODO: Shouldn't local users stay available on // connection loss? We probably need to fix this // in infinote. break; } } static void on_user_notify_status_static(InfUser* user, GParamSpec* pspec, gpointer user_data) { static_cast(user_data)->on_user_notify_status(user); } void on_active_user_changed(InfUser* user) { if(m_active_user != NULL) { if(m_active) deactivate_user(); g_signal_handler_disconnect(G_OBJECT(m_active_user), m_notify_status_handle); } m_active_user = user; if(user != NULL) { InfUserStatus user_status = inf_user_get_status(INF_USER(user)); g_assert(user_status != INF_USER_UNAVAILABLE); m_notify_status_handle = g_signal_connect( G_OBJECT(user), "notify::status", G_CALLBACK(&on_user_notify_status_static), this ); if( (user_status == INF_USER_ACTIVE && !m_active)) { deactivate_user(); } else if(user_status == INF_USER_INACTIVE && m_active) { activate_user(); } } } void on_user_notify_status(InfUser* user) { // User cannot be activated when we are not active g_assert(m_active || inf_user_get_status(user) != INF_USER_ACTIVE); if(inf_user_get_status(user) == INF_USER_ACTIVE && m_active) { // The user did something (therefore becoming active), // so we do not need to explictely activate the user. g_assert(m_timeout_connection.connected()); m_timeout_connection.disconnect(); } } bool on_activation_timeout() { // The user activated this document, but did not something for // a while, so explicitely set the user active g_assert(m_active); g_assert(m_active_user != NULL); g_assert(inf_user_get_status(m_active_user) == INF_USER_INACTIVE); inf_session_set_user_status( INF_SESSION(m_view.get_session()), m_active_user, INF_USER_ACTIVE); return false; } SessionView& m_view; InfUser* m_active_user; bool m_active; sigc::connection m_timeout_connection; gulong m_notify_status_handle; }; class Gobby::FolderCommands::TextDocInfo: public Gobby::FolderCommands::DocInfo { public: TextDocInfo(TextSessionView& view): DocInfo(view) {} virtual void activate() { DocInfo::activate(); InfTextGtkBuffer* buffer = INF_TEXT_GTK_BUFFER( inf_session_get_buffer(m_view.get_session())); inf_text_gtk_buffer_set_wake_on_cursor_movement( buffer, TRUE); } virtual void deactivate() { DocInfo::deactivate(); InfTextGtkBuffer* buffer = INF_TEXT_GTK_BUFFER( inf_session_get_buffer(m_view.get_session())); inf_text_gtk_buffer_set_wake_on_cursor_movement( buffer, FALSE); } virtual void flush() { DocInfo::flush(); g_assert(m_active_user != NULL); inf_text_session_flush_requests_for_user( INF_TEXT_SESSION(m_view.get_session()), INF_TEXT_USER(m_active_user)); } }; Gobby::FolderCommands::FolderCommands(const Folder& folder): m_folder(folder), m_current_view(NULL) { m_folder.signal_document_added().connect( sigc::mem_fun(*this, &FolderCommands::on_document_added)); m_folder.signal_document_removed().connect( sigc::mem_fun(*this, &FolderCommands::on_document_removed)); m_folder.signal_document_changed().connect( sigc::mem_fun(*this, &FolderCommands::on_document_changed)); const unsigned int n_pages = static_cast(m_folder.get_n_pages()); for(unsigned int i = 0; i < n_pages; ++i) { // TODO: Convenience API in Folder to retrieve SessionView, // so that we don't need to know about SessionUserView here. const SessionUserView* user_view = static_cast( m_folder.get_nth_page(i)); SessionView& view = user_view->get_session_view(); on_document_added(view); } on_document_changed(m_folder.get_current_document()); } Gobby::FolderCommands::~FolderCommands() { for(DocumentMap::iterator iter = m_doc_map.begin(); iter != m_doc_map.end(); ++ iter) { delete iter->second; } } void Gobby::FolderCommands::on_document_added(SessionView& view) { DocInfo* info; { TextSessionView* text_view = dynamic_cast(&view); if(text_view) info = new TextDocInfo(*text_view); else info = new DocInfo(view); } m_doc_map[&view] = info; } void Gobby::FolderCommands::on_document_removed(SessionView& view) { DocumentMap::iterator iter = m_doc_map.find(&view); g_assert(iter != m_doc_map.end()); delete iter->second; m_doc_map.erase(iter); // TODO: Isn't this called by Folder already? Would need to // call changed first and then removed of course. We could // then assert here. if(&view == m_current_view) m_current_view = NULL; } void Gobby::FolderCommands::on_document_changed(SessionView* view) { if(m_current_view != NULL) { DocumentMap::iterator iter = m_doc_map.find(m_current_view); g_assert(iter != m_doc_map.end()); iter->second->deactivate(); } m_current_view = view; if(m_current_view != NULL) { DocumentMap::iterator iter = m_doc_map.find(m_current_view); g_assert(iter != m_doc_map.end()); iter->second->activate(); } } code/commands/folder-commands.hpp000066400000000000000000000027051304552132100173540ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_FOLDER_COMMANDS_HPP_ #define _GOBBY_FOLDER_COMMANDS_HPP_ #include "core/folder.hpp" #include namespace Gobby { class FolderCommands: public sigc::trackable { public: FolderCommands(const Folder& folder); ~FolderCommands(); protected: void on_document_added(SessionView& view); void on_document_removed(SessionView& view); void on_document_changed(SessionView* view); const Folder& m_folder; SessionView* m_current_view; class DocInfo; typedef std::map DocumentMap; DocumentMap m_doc_map; class TextDocInfo; }; } #endif // _GOBBY_FOLDER_COMMANDS_HPP_ code/commands/help-commands.cpp000066400000000000000000000111631304552132100170220ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "commands/help-commands.hpp" #include "util/i18n.hpp" #include "features.hpp" #include #include Gobby::HelpCommands::HelpCommands(Gtk::Application& application, const ApplicationActions& actions): m_application(application) { #ifndef HAVE_GNOME_DOC_UTILS actions.help->set_enabled(false); #endif actions.help->signal_activate().connect( sigc::hide(sigc::mem_fun(*this, &HelpCommands::on_contents))); actions.about->signal_activate().connect( sigc::hide(sigc::mem_fun(*this, &HelpCommands::on_about))); } void Gobby::HelpCommands::on_contents() { GError* error = NULL; Gtk::Window* parent = m_application.get_windows()[0]; gtk_show_uri(parent->get_screen()->gobj(), "ghelp:gobby", GDK_CURRENT_TIME, &error); if(error == NULL) return; // Help browser could not be invoked, show an error message to the user. Gtk::MessageDialog dlg(*parent, _("There was an error displaying help."), false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true); dlg.set_secondary_text(error->message); dlg.run(); g_error_free(error); } void Gobby::HelpCommands::on_about() { if(m_about_dialog.get() == NULL) { Gtk::Window* parent = m_application.get_windows()[0]; m_about_dialog.reset(new Gtk::AboutDialog); m_about_dialog->set_transient_for(*parent); std::vector artists; artists.push_back("Benjamin Herr "); artists.push_back("Thomas Glatt "); std::vector authors; authors.push_back("Armin Burgmeier "); authors.push_back("Philipp Kern "); authors.push_back(""); authors.push_back(_("Contributors:")); authors.push_back("\tBenjamin Herr "); authors.push_back("\tBen Levitt "); authors.push_back("\tGabríel A. Pétursson "); std::vector translators; translators.push_back(_("British English:")); translators.push_back("\tGabríel A. Pétursson "); translators.push_back(_("German:")); translators.push_back("\tMichael Frey "); Glib::ustring transl = ""; for(std::vector::iterator i = translators.begin(); i != translators.end(); ++i) { if(i != translators.begin()) transl += "\n"; transl += *i; } m_about_dialog->set_artists(artists); m_about_dialog->set_authors(authors); m_about_dialog->set_translator_credits(transl); m_about_dialog->set_copyright( "Copyright © 2008-2015 Armin Burgmeier"); m_about_dialog->set_license(_( "Permission to use, copy, modify, and/or distribute " "this software for any urpose with or without fee is " "hereby granted, provided that the above copyright " "notice and this permission notice appear in all " "copies.\n\n" "THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR " "DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS " "SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF " "MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE " "AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, " "OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER " "RESULTING FROM LOSS OF USE, DATA OR PROFITS, " "WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR " "OTHER TORTIOUS ACTION, ARISING OUT OF OR IN " "CONNECTION WITH THE USE OR PERFORMANCE OF THIS " "SOFTWARE.")); m_about_dialog->set_logo_icon_name("gobby-0.5"); m_about_dialog->set_program_name("Gobby"); m_about_dialog->set_version(PACKAGE_VERSION); m_about_dialog->set_website("http://gobby.0x539.de/"); m_about_dialog->set_wrap_license(true); m_about_dialog->signal_response().connect( sigc::mem_fun( *this, &HelpCommands::on_about_response)); } m_about_dialog->present(); } void Gobby::HelpCommands::on_about_response(int id) { m_about_dialog.reset(NULL); } code/commands/help-commands.hpp000066400000000000000000000026541304552132100170340ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_HELP_COMMANDS_HPP_ #define _GOBBY_HELP_COMMANDS_HPP_ #include "core/applicationactions.hpp" #include #include #include #include namespace Gobby { class HelpCommands: public sigc::trackable { public: HelpCommands(Gtk::Application& application, const ApplicationActions& actions); protected: void on_contents(); void on_about(); void on_about_response(int response_id); Gtk::Application& m_application; std::unique_ptr m_about_dialog; }; } #endif // _GOBBY_HELP_COMMANDS_HPP_ code/commands/subscription-commands.cpp000066400000000000000000000117321304552132100206200ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "commands/subscription-commands.hpp" #include "util/i18n.hpp" class Gobby::SubscriptionCommands::SessionInfo { public: SessionInfo(SubscriptionCommands& commands, const Folder& folder, InfSession* session): m_folder(folder), m_session(session) { g_object_ref(session); m_notify_subscription_group_handler = g_signal_connect( G_OBJECT(session), "notify::subscription-group", G_CALLBACK(on_notify_subscription_group_static), &commands); } ~SessionInfo() { g_signal_handler_disconnect( G_OBJECT(m_session), m_notify_subscription_group_handler); g_object_unref(m_session); } const Folder& get_folder() { return m_folder; } InfSession* get_session() { return m_session; } private: static void on_notify_subscription_group_static(InfSession* session, GParamSpec* pspec, gpointer user_data) { static_cast(user_data)-> on_notify_subscription_group(session); } const Folder& m_folder; InfSession* m_session; gulong m_notify_subscription_group_handler; }; Gobby::SubscriptionCommands::SubscriptionCommands(const Folder& text_folder, const Folder& chat_folder): m_text_folder(text_folder), m_chat_folder(chat_folder) { m_text_folder.signal_document_added().connect( sigc::mem_fun( *this, &SubscriptionCommands::on_text_document_added)); m_chat_folder.signal_document_added().connect( sigc::mem_fun( *this, &SubscriptionCommands::on_chat_document_added)); m_text_folder.signal_document_removed().connect( sigc::mem_fun( *this, &SubscriptionCommands::on_document_removed)); m_chat_folder.signal_document_removed().connect( sigc::mem_fun( *this, &SubscriptionCommands::on_document_removed)); } Gobby::SubscriptionCommands::~SubscriptionCommands() { for(SessionMap::iterator iter = m_session_map.begin(); iter != m_session_map.end(); ++iter) { delete iter->second; } } void Gobby::SubscriptionCommands::on_text_document_added(SessionView& view) { InfSession* session = view.get_session(); g_assert(m_session_map.find(session) == m_session_map.end()); m_session_map[session] = new SessionInfo(*this, m_text_folder, session); } void Gobby::SubscriptionCommands::on_chat_document_added(SessionView& view) { InfSession* session = view.get_session(); g_assert(m_session_map.find(session) == m_session_map.end()); m_session_map[session] = new SessionInfo(*this, m_chat_folder, session); } void Gobby::SubscriptionCommands::on_document_removed(SessionView& view) { InfSession* session = view.get_session(); SessionMap::iterator iter = m_session_map.find(session); g_assert(iter != m_session_map.end()); delete iter->second; m_session_map.erase(iter); } void Gobby::SubscriptionCommands:: on_notify_subscription_group(InfSession* session) { SessionMap::iterator iter = m_session_map.find(session); g_assert(iter != m_session_map.end()); if(inf_session_get_subscription_group(session) == NULL) { const Folder& folder = iter->second->get_folder(); SessionView* view = folder.lookup_document(session); g_assert(view != NULL); TextSessionView* text_view = dynamic_cast(view); ChatSessionView* chat_view = dynamic_cast(view); if(text_view) { /* If the session is in SYNCHRONIZING state then the * session is closed due to a synchronization error. * In that case synchronization-commands.cpp will set * a more meaningful error message. */ if(inf_session_get_status(session) == INF_SESSION_RUNNING) { view->set_info(_( "The connection to the publisher of " "this document has been lost. " "Further changes to the document " "could not be synchronized to others " "anymore, therefore the document " "cannot be edited anymore.\n\n" "Please note also that it is " "possible that not all of your " "latest changes have reached the " "publisher before the connection was " "lost."), true); } text_view->set_active_user(NULL); } else if(chat_view) { chat_view->set_active_user(NULL); } } } code/commands/subscription-commands.hpp000066400000000000000000000034431304552132100206250ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_SUBSCRIPTION_COMMANDS_HPP_ #define _GOBBY_SUBSCRIPTION_COMMANDS_HPP_ #include "core/folder.hpp" namespace Gobby { // This class does not do much anymore. All it does is showing an error // message when a session loses the connection to its publisher, and resets // the active user to NULL. // TODO: The name of this class is a bit misleading now. class SubscriptionCommands: public sigc::trackable { public: SubscriptionCommands(const Folder& text_folder, const Folder& chat_folder); ~SubscriptionCommands(); protected: void on_text_document_added(SessionView& view); void on_chat_document_added(SessionView& view); void on_document_removed(SessionView& view); void on_notify_subscription_group(InfSession* session); const Folder& m_text_folder; const Folder& m_chat_folder; class SessionInfo; typedef std::map SessionMap; SessionMap m_session_map; }; } #endif // _GOBBY_SUBSCRIPTION_COMMANDS_HPP_ code/commands/synchronization-commands.cpp000066400000000000000000000174351304552132100213430ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "commands/synchronization-commands.hpp" #include "util/i18n.hpp" namespace { inline const gchar* _(const gchar* msgid) { return Gobby::_(msgid); } void set_progress_text(Gobby::SessionView& view, gdouble percentage) { view.set_info( Glib::ustring::compose( _("Synchronization in progress... %1%%"), static_cast(percentage * 100)), false); } void set_error_text(Gobby::SessionView& view, const Glib::ustring& initial_text) { Glib::ustring type_text; // Document cannot be used if an error happened // during synchronization. type_text = _("This document cannot be used."); const Glib::ustring info_text = _("If you have an idea what could have caused the " "problem, then you may attempt to solve it and " "try again (after having closed this document). " "Otherwise it is most likely a bug in the " "software. In that case, please file a bug report " "at http://gobby.0x539.de/trac/newticket and " "provide as much information as you can, including " "what you did when the problem occurred and how to " "reproduce the problem (if possible) so that we " "can fix the problem in a later version. " "Thank you."); view.set_info( Glib::ustring::compose( _("Synchronization failed: %1"), initial_text) + "\n\n" + type_text + "\n\n" + info_text, true); } } class Gobby::SynchronizationCommands::SyncInfo { public: SyncInfo(SynchronizationCommands& sync_commands, SessionView& view); ~SyncInfo(); SessionView& get_session_view() { return m_view; } private: static void on_synchronization_failed_static(InfSession* session, InfXmlConnection* conn, const GError* error, gpointer user_data) { static_cast(user_data)-> on_synchronization_failed(session, conn, error); } static void on_synchronization_complete_static(InfSession* session, InfXmlConnection* conn, gpointer user_data) { static_cast(user_data)-> on_synchronization_complete(session, conn); } static void on_synchronization_progress_static(InfSession* session, InfXmlConnection* conn, gdouble percentage, gpointer user_data) { static_cast(user_data)-> on_synchronization_progress(conn, percentage); } void on_synchronization_progress(InfXmlConnection* conn, gdouble percentage); SessionView& m_view; gulong m_synchronization_complete_handler; gulong m_synchronization_failed_handler; gulong m_synchronization_progress_handler; }; Gobby::SynchronizationCommands::SyncInfo:: SyncInfo(SynchronizationCommands& commands, SessionView& view): m_view(view) { InfSession* session = m_view.get_session(); m_synchronization_complete_handler = g_signal_connect( G_OBJECT(session), "synchronization-complete", G_CALLBACK(on_synchronization_complete_static), &commands); m_synchronization_failed_handler = g_signal_connect( G_OBJECT(session), "synchronization-failed", G_CALLBACK(on_synchronization_failed_static), &commands); m_synchronization_progress_handler = g_signal_connect( G_OBJECT(session), "synchronization-progress", G_CALLBACK(on_synchronization_progress_static), this); } Gobby::SynchronizationCommands::SyncInfo::~SyncInfo() { InfSession* session = m_view.get_session(); g_signal_handler_disconnect(G_OBJECT(session), m_synchronization_complete_handler); g_signal_handler_disconnect(G_OBJECT(session), m_synchronization_failed_handler); g_signal_handler_disconnect(G_OBJECT(session), m_synchronization_progress_handler); } void Gobby::SynchronizationCommands::SyncInfo:: on_synchronization_progress(InfXmlConnection* conn, gdouble percentage) { set_progress_text(m_view, percentage); } Gobby::SynchronizationCommands:: SynchronizationCommands(const Folder& text_folder, const Folder& chat_folder) { text_folder.signal_document_added().connect( sigc::mem_fun( *this, &SynchronizationCommands::on_document_added)); chat_folder.signal_document_added().connect( sigc::mem_fun( *this, &SynchronizationCommands::on_document_added)); text_folder.signal_document_removed().connect( sigc::mem_fun( *this, &SynchronizationCommands::on_document_removed)); text_folder.signal_document_removed().connect( sigc::mem_fun( *this, &SynchronizationCommands::on_document_removed)); } Gobby::SynchronizationCommands::~SynchronizationCommands() { for(SyncMap::iterator iter = m_sync_map.begin(); iter != m_sync_map.end(); ++iter) { delete iter->second; } } void Gobby::SynchronizationCommands::on_document_added(SessionView& view) { InfSession* session = view.get_session(); if(inf_session_get_status(session) == INF_SESSION_SYNCHRONIZING) { InfXmlConnection* connection; g_object_get(G_OBJECT(session), "sync-connection", &connection, NULL); gdouble percentage = inf_session_get_synchronization_progress( session, connection); g_object_unref(connection); g_assert(m_sync_map.find(session) == m_sync_map.end()); m_sync_map[session] = new SyncInfo(*this, view); set_progress_text(view, percentage); } } void Gobby::SynchronizationCommands::on_document_removed(SessionView& view) { InfSession* session = view.get_session(); SyncMap::iterator iter = m_sync_map.find(session); if(iter != m_sync_map.end()) { delete iter->second; m_sync_map.erase(iter); } } void Gobby::SynchronizationCommands:: on_synchronization_failed(InfSession* session, InfXmlConnection* c, const GError* error) { SyncMap::iterator iter = m_sync_map.find(session); g_assert(iter != m_sync_map.end()); set_error_text(iter->second->get_session_view(), error->message); // The document will be of no use anyway, so consider it as not // being modified. InfBuffer* buffer = inf_session_get_buffer(session); inf_buffer_set_modified(buffer, FALSE); delete iter->second; m_sync_map.erase(iter); } void Gobby::SynchronizationCommands:: on_synchronization_complete(InfSession* session, InfXmlConnection* c) { SyncMap::iterator iter = m_sync_map.find(session); g_assert(iter != m_sync_map.end()); // TODO: Actually we should always set the modified flag, except the // document is either empty, or known in the document info storage // and the version on disk is the same as the one we got // synchronized. We could store a hash and modification time in the // documentinfo storage for this. InfBuffer* buffer = inf_session_get_buffer(session); inf_buffer_set_modified(buffer, FALSE); delete iter->second; m_sync_map.erase(iter); } code/commands/synchronization-commands.hpp000066400000000000000000000033251304552132100213410ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_SYNCHRONIZATION_COMMANDS_HPP_ #define _GOBBY_SYNCHRONIZATION_COMMANDS_HPP_ #include #include "core/folder.hpp" namespace Gobby { class SynchronizationCommands: public sigc::trackable { public: SynchronizationCommands(const Folder& text_folder, const Folder& chat_folder); ~SynchronizationCommands(); protected: class SyncInfo; void on_document_added(SessionView& view); void on_document_removed(SessionView& view); void on_synchronization_failed(InfSession* session, InfXmlConnection* connection, const GError* error); void on_synchronization_complete(InfSession* session, InfXmlConnection* connection); typedef std::map SyncMap; SyncMap m_sync_map; }; } #endif // _GOBBY_SYNCHRONIZATION_COMMANDS_HPP_ code/commands/user-join-commands.cpp000066400000000000000000000270301304552132100200050ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "commands/user-join-commands.hpp" #include "core/nodewatch.hpp" #include "util/i18n.hpp" #include #include #include #include namespace { inline const gchar* _(const gchar* msgid) { return Gobby::_(msgid); } void set_error_text(Gobby::SessionView& view, const Glib::ustring& initial_text) { using namespace Gobby; Glib::ustring type_text; // TODO: Adjust this for chat sessions type_text = _("You can still watch others editing " "the document, but you cannot edit " "it yourself."); const Glib::ustring info_text = _("If you have an idea what could have caused the " "problem, then you may attempt to solve it and " "try again (after having closed this document). " "Otherwise it is most likely a bug in the " "software. In that case, please file a bug report " "at http://gobby.0x539.de/trac/newticket and " "provide as much information as you can, including " "what you did when the problem occurred and how to " "reproduce the problem (if possible) so that we " "can fix the problem in a later version. " "Thank you."); view.set_info( Glib::ustring::compose( _("User Join failed: %1"), initial_text) + "\n\n" + type_text + "\n\n" + info_text, true); } void set_permission_denied_text(Gobby::SessionView& view) { view.set_info( _("Permissions are not granted to modify the document.") , true); } class ParameterProvider: public Gobby::UserJoin::ParameterProvider { public: ParameterProvider(Gobby::SessionView& view, Gobby::Folder& folder, const Gobby::Preferences& preferences): m_view(view), m_folder(folder), m_preferences(preferences) { } virtual std::vector get_user_join_parameters(); protected: void add_text_parameters(std::vector& parameters, Gobby::TextSessionView& view); Gobby::SessionView& m_view; Gobby::Folder& m_folder; const Gobby::Preferences& m_preferences; }; std::vector ParameterProvider::get_user_join_parameters() { // Otherwise join a new user. std::vector params; const GParameter name_param = { "name", { 0 } }; params.push_back(name_param); const GParameter status_param = { "status", { 0 } }; params.push_back(status_param); g_value_init(¶ms[0].value, G_TYPE_STRING); g_value_init(¶ms[1].value, INF_TYPE_USER_STATUS); const Glib::ustring& pref_name = m_preferences.user.name; g_value_set_string(¶ms[0].value, pref_name.c_str()); if(m_folder.get_current_document() == &m_view) g_value_set_enum(¶ms[1].value, INF_USER_ACTIVE); else g_value_set_enum(¶ms[1].value, INF_USER_INACTIVE); Gobby::TextSessionView* text_view = dynamic_cast(&m_view); if(text_view) add_text_parameters(params, *text_view); return params; } void ParameterProvider::add_text_parameters( std::vector& params, Gobby::TextSessionView& view) { InfTextSession* session = view.get_session(); GParameter hue_param = { "hue", { 0 } }; g_value_init(&hue_param.value, G_TYPE_DOUBLE); g_value_set_double(&hue_param.value, m_preferences.user.hue); params.push_back(hue_param); GParameter vector_param = { "vector", { 0 } }; g_value_init(&vector_param.value, INF_ADOPTED_TYPE_STATE_VECTOR); g_value_take_boxed(&vector_param.value, inf_adopted_state_vector_copy( inf_adopted_algorithm_get_current( inf_adopted_session_get_algorithm( INF_ADOPTED_SESSION( session))))); params.push_back(vector_param); GParameter caret_param = { "caret-position", { 0 } }; g_value_init(&caret_param.value, G_TYPE_UINT); GtkTextBuffer* buffer = GTK_TEXT_BUFFER(view.get_text_buffer()); GtkTextMark* mark = gtk_text_buffer_get_insert(buffer); GtkTextIter caret_iter; gtk_text_buffer_get_iter_at_mark(buffer, &caret_iter, mark); g_value_set_uint(&caret_param.value, gtk_text_iter_get_offset(&caret_iter)); params.push_back(caret_param); } } class Gobby::UserJoinCommands::UserJoinInfo { public: UserJoinInfo(UserJoinCommands& commands, std::unique_ptr userjoin, Folder& folder, SessionView& view); private: void on_user_join_finished(InfUser* user, const GError* error); UserJoinCommands& m_commands; std::unique_ptr m_userjoin; Folder& m_folder; SessionView& m_view; }; Gobby::UserJoinCommands::UserJoinInfo::UserJoinInfo(UserJoinCommands& cmds, std::unique_ptr j, Folder& folder, SessionView& view): m_commands(cmds), m_userjoin(std::move(j)), m_folder(folder), m_view(view) { // Only if userjoin is still running: g_assert(m_userjoin->get_user() == NULL); g_assert(m_userjoin->get_error() == NULL); // Wait for userjoin to finish m_userjoin->signal_finished().connect( sigc::mem_fun( *this, &UserJoinInfo::on_user_join_finished)); } void Gobby::UserJoinCommands::UserJoinInfo:: on_user_join_finished(InfUser* user, const GError* error) { m_commands.on_user_join_finished( m_userjoin->get_proxy(), m_folder, m_view, user, error); // Note that the above call deletes this object! } Gobby::UserJoinCommands::UserJoinCommands(FolderManager& folder_manager, const Preferences& preferences): m_preferences(preferences) { folder_manager.signal_document_added().connect( sigc::mem_fun( *this, &UserJoinCommands::on_document_added)); folder_manager.signal_document_removed().connect( sigc::mem_fun( *this, &UserJoinCommands::on_document_removed)); } Gobby::UserJoinCommands::~UserJoinCommands() { for(UserJoinMap::iterator iter = m_user_join_map.begin(); iter != m_user_join_map.end(); ++iter) { delete iter->second; } } void Gobby::UserJoinCommands::on_document_added(InfBrowser* browser, const InfBrowserIter* iter, InfSessionProxy* proxy, Folder& folder, SessionView& view, FolderManager::UserJoinRef j) { g_assert(proxy != NULL); g_assert(m_user_join_map.find(proxy) == m_user_join_map.end()); std::unique_ptr userjoin; if(j && j->get()) { // If there is a user already joined for this session, // then simply use that user instead of joining another one. userjoin = std::move(*j); } else { // Otherwise join a new user. std::unique_ptr provider( new ParameterProvider(view, folder, m_preferences)); userjoin.reset(new UserJoin(browser, iter, proxy, std::move(provider))); } if(userjoin->get_user() == NULL && userjoin->get_error() == NULL) { m_user_join_map[proxy] = new UserJoinInfo(*this, std::move(userjoin), folder, view); } else { on_user_join_finished( proxy, folder, view, userjoin->get_user(), userjoin->get_error()); } } void Gobby::UserJoinCommands::on_document_removed(InfBrowser* browser, const InfBrowserIter* iter, InfSessionProxy* proxy, Folder& folder, SessionView& view) { g_assert(proxy != NULL); UserJoinMap::iterator user_iter = m_user_join_map.find(proxy); // If the user join was successful the session is no longer in the map if(user_iter != m_user_join_map.end()) { delete user_iter->second; m_user_join_map.erase(user_iter); } else { // The user has removed the document. What we do now depends // on whether we are hosting the document or whether we are a // client. If we are a client we reset the connection of the // session proxy, which basically leads to us being // unsubscribed from the document. If we are hosting the // document, we do not want to unsubscribe from it, since // other users might still be connected. We therefore only // remove the local user from the session. If there are indeed // no other clients, then InfdDirectory will take care of // unsubscribing the session 60s after it became idle. if(INFD_IS_SESSION_PROXY(proxy)) { InfUser* user = view.get_active_user(); if(user != NULL) { InfSession* session; g_object_get(G_OBJECT(proxy), "session", &session, NULL); inf_session_set_user_status( session, user, INF_USER_UNAVAILABLE); g_object_unref(session); // TODO: set_active_user should go to // SessionView base: // TODO: The libinftextgtk objects should // reset the active user automatically when it // becomes unavailable. TextSessionView* text_view = dynamic_cast(&view); if(text_view) text_view->set_active_user(NULL); ChatSessionView* chat_view = dynamic_cast(&view); if(chat_view) chat_view->set_active_user(NULL); } } else if(INFC_IS_SESSION_PROXY(proxy)) { infc_session_proxy_set_connection( INFC_SESSION_PROXY(proxy), NULL, NULL, 0); } } } void Gobby::UserJoinCommands::on_user_join_finished(InfSessionProxy* proxy, Folder& folder, SessionView& view, InfUser* user, const GError* error) { g_assert(user != NULL || error != NULL); // Remove userjoin object. It might not exist if // on_document_added() was passed a user immediately. UserJoinMap::iterator user_iter = m_user_join_map.find(proxy); if(user_iter != m_user_join_map.end()) { delete user_iter->second; m_user_join_map.erase(user_iter); } if(error == NULL) { // TODO: Notify the user about alternative user name if s/he uses any view.unset_info(); // TODO: set_active_user should maybe go to SessionView base: TextSessionView* text_view = dynamic_cast(&view); if(text_view) text_view->set_active_user(INF_TEXT_USER(user)); ChatSessionView* chat_view = dynamic_cast(&view); if(chat_view) chat_view->set_active_user(user); } else if(error->domain == inf_request_error_quark() && error->code == INF_REQUEST_ERROR_NOT_AUTHORIZED) { set_permission_denied_text(view); } else { set_error_text(view, error->message); } } code/commands/user-join-commands.hpp000066400000000000000000000042531304552132100200140ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_USER_JOIN_COMMANDS_HPP_ #define _GOBBY_USER_JOIN_COMMANDS_HPP_ #include "core/foldermanager.hpp" #include "core/preferences.hpp" #include "core/userjoin.hpp" #include namespace Gobby { class UserJoinCommands: public sigc::trackable { public: UserJoinCommands(FolderManager& folder_manager, const Preferences& preferences); ~UserJoinCommands(); protected: void on_document_added(InfBrowser* browser, const InfBrowserIter* iter, InfSessionProxy* proxy, Folder& folder, SessionView& view, FolderManager::UserJoinRef j); void on_document_removed(InfBrowser* browser, const InfBrowserIter* iter, InfSessionProxy* proxy, Folder& folder, SessionView& view); void on_user_join_finished(InfSessionProxy* proxy, Folder& folder, SessionView& view, InfUser* user, const GError* error); const Preferences& m_preferences; class UserJoinInfo; typedef std::map UserJoinMap; UserJoinMap m_user_join_map; }; } #endif // _GOBBY_USER_JOIN_COMMANDS_HPP_ code/commands/view-commands.cpp000066400000000000000000000356261304552132100170560ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "commands/view-commands.hpp" #include "util/i18n.hpp" #include #include class Gobby::ViewCommands::Fullscreen { public: class Preserve: public sigc::trackable { public: Preserve(const Glib::RefPtr& action); ~Preserve(); private: void on_toggled(); Glib::RefPtr m_action; bool m_value; }; Fullscreen(ViewCommands& commands); ~Fullscreen(); private: ViewCommands& m_commands; Preserve m_preserve_toolbar; Preserve m_preserve_browser; Preserve m_preserve_chat; Preserve m_preserve_document_userlist; Preserve m_preserve_chat_userlist; }; Gobby::ViewCommands::Fullscreen::Preserve::Preserve( const Glib::RefPtr& action) : m_action(action) { g_assert(action->get_state_type().equal( Glib::VariantType(G_VARIANT_TYPE_BOOLEAN))); // De-activate all the clutter, but remember their state before // we went fullscreen, so that we can restore when we leave // fullscreen again. action->get_state(m_value); action->change_state(false); action->property_state().signal_changed().connect( sigc::mem_fun(*this, &Preserve::on_toggled)); } Gobby::ViewCommands::Fullscreen::Preserve::~Preserve() { m_action->change_state(m_value); } void Gobby::ViewCommands::Fullscreen::Preserve::on_toggled() { // Update value; if one of the options is toggled we keep the // toggled version when fullscreen is left. m_action->get_state(m_value); } Gobby::ViewCommands::Fullscreen::Fullscreen(ViewCommands& commands): m_commands(commands), m_preserve_toolbar(commands.m_actions.view_toolbar), m_preserve_browser(commands.m_actions.view_browser), m_preserve_chat(commands.m_actions.view_chat), m_preserve_document_userlist( commands.m_actions.view_document_userlist), m_preserve_chat_userlist( commands.m_actions.view_chat_userlist) { m_commands.m_parent.fullscreen(); } Gobby::ViewCommands::Fullscreen::~Fullscreen() { m_commands.m_parent.unfullscreen(); } Gobby::ViewCommands::ViewCommands(Gtk::Window& parent, WindowActions& actions, GtkSourceLanguageManager* language_manager, const Folder& text_folder, ClosableFrame& chat_frame, const Folder& chat_folder, Preferences& preferences): m_parent(parent), m_actions(actions), m_language_manager(language_manager), m_text_folder(text_folder), m_chat_frame(chat_frame), m_chat_folder(chat_folder), m_preferences(preferences), m_current_view(NULL) { actions.hide_user_colors->signal_activate().connect(sigc::hide( sigc::mem_fun(*this, &ViewCommands::on_hide_user_colors))); actions.fullscreen->property_state().signal_changed().connect( sigc::mem_fun(*this, &ViewCommands::on_fullscreen_toggled)); actions.zoom_in->signal_activate().connect(sigc::hide( sigc::mem_fun(*this, &ViewCommands::on_zoom_in))); actions.zoom_out->signal_activate().connect(sigc::hide( sigc::mem_fun(*this, &ViewCommands::on_zoom_out))); m_menu_view_toolbar_connection = actions.view_toolbar-> property_state().signal_changed().connect( sigc::mem_fun( *this, &ViewCommands::on_menu_toolbar_toggled)); m_menu_view_statusbar_connection = actions.view_statusbar-> property_state().signal_changed().connect( sigc::mem_fun( *this, &ViewCommands::on_menu_statusbar_toggled)); m_menu_view_browser_connection = actions.view_browser-> property_state().signal_changed().connect( sigc::mem_fun( *this, &ViewCommands::on_menu_browser_toggled)); m_menu_view_chat_connection = actions.view_chat-> property_state().signal_changed().connect( sigc::mem_fun( *this, &ViewCommands::on_menu_chat_toggled)); m_menu_view_document_userlist_connection = actions.view_document_userlist-> property_state().signal_changed().connect(sigc::mem_fun( *this, &ViewCommands:: on_menu_document_userlist_toggled)); m_menu_view_chat_userlist_connection = actions.view_chat_userlist-> property_state().signal_changed().connect( sigc::mem_fun( *this, &ViewCommands:: on_menu_chat_userlist_toggled)); // Shortcut: Preferences::Appearance& appearance = preferences.appearance; m_pref_view_toolbar_connection = appearance.show_toolbar.signal_changed().connect( sigc::mem_fun( *this, &ViewCommands::on_pref_toolbar_changed)); m_pref_view_statusbar_connection = appearance.show_statusbar.signal_changed().connect( sigc::mem_fun( *this, &ViewCommands::on_pref_statusbar_changed)); m_pref_view_browser_connection = appearance.show_browser.signal_changed().connect( sigc::mem_fun( *this, &ViewCommands::on_pref_browser_changed)); m_pref_view_chat_connection = appearance.show_chat.signal_changed().connect( sigc::mem_fun( *this, &ViewCommands::on_pref_chat_changed)); m_pref_view_document_userlist_connection = appearance.show_document_userlist.signal_changed().connect( sigc::mem_fun( *this, &ViewCommands:: on_pref_document_userlist_changed)); m_pref_view_chat_userlist_connection = appearance.show_chat_userlist.signal_changed().connect( sigc::mem_fun( *this, &ViewCommands:: on_pref_chat_userlist_changed)); m_text_folder.signal_document_changed().connect( sigc::mem_fun( *this, &ViewCommands::on_text_document_changed)); m_chat_folder.signal_document_added().connect( sigc::mem_fun( *this, &ViewCommands::on_chat_document_added)); m_chat_folder.signal_document_removed().connect( sigc::mem_fun( *this, &ViewCommands::on_chat_document_removed)); m_chat_folder.signal_document_changed().connect( sigc::mem_fun( *this, &ViewCommands::on_chat_document_changed)); m_menu_language_changed_connection = actions.highlight_mode-> property_state().signal_changed().connect( sigc::mem_fun( *this, &ViewCommands::on_menu_language_changed)); m_chat_frame.signal_show().connect( sigc::mem_fun(*this, &ViewCommands::on_chat_show)); m_chat_frame.signal_hide().connect( sigc::mem_fun(*this, &ViewCommands::on_chat_hide)); // Chat View by default not sensitive, becomes sensitive if a server // connection is made. actions.view_chat->set_enabled(false); m_chat_frame.set_allow_visible(false); // Setup initial sensitivity: on_text_document_changed(m_text_folder.get_current_document()); on_chat_document_changed(m_chat_folder.get_current_document()); } Gobby::ViewCommands::~ViewCommands() { // Disconnect handlers from current document: on_text_document_changed(NULL); on_chat_document_changed(NULL); } void Gobby::ViewCommands::on_text_document_changed(SessionView* view) { if(m_current_view != NULL) m_document_language_changed_connection.disconnect(); m_current_view = dynamic_cast(view); if(m_current_view != NULL) { m_actions.hide_user_colors->set_enabled(true); m_actions.zoom_in->set_enabled(true); m_actions.zoom_out->set_enabled(true); m_actions.highlight_mode->set_enabled(true); m_actions.view_document_userlist->set_enabled(true); m_document_language_changed_connection = m_current_view->signal_language_changed().connect( sigc::mem_fun( *this, &ViewCommands:: on_doc_language_changed)); } else { m_actions.hide_user_colors->set_enabled(false); m_actions.zoom_in->set_enabled(false); m_actions.zoom_out->set_enabled(false); m_menu_language_changed_connection.block(); m_actions.highlight_mode->set_enabled(false); m_actions.highlight_mode->change_state( Glib::Variant::create("")); m_menu_language_changed_connection.unblock(); m_actions.view_document_userlist->set_enabled(false); } on_doc_language_changed( m_current_view ? m_current_view->get_language() : NULL); } void Gobby::ViewCommands::on_chat_document_added(SessionView& view) { // Allow the chat frame to be visible if the option allows it m_chat_frame.set_allow_visible(true); m_actions.view_chat->set_enabled(true); } void Gobby::ViewCommands::on_chat_document_removed(SessionView& view) { if(m_chat_folder.get_n_pages() == 1) { // This is the last document, and it is about to be removed. m_actions.view_chat->set_enabled(false); // Hide the chat frame independent of the option m_chat_frame.set_allow_visible(false); } } void Gobby::ViewCommands::on_chat_document_changed(SessionView* view) { if(view != NULL) { if(m_chat_frame.get_visible()) { m_actions.view_chat_userlist->set_enabled(true); } } else { m_actions.view_chat_userlist->set_enabled(false); } } void Gobby::ViewCommands::on_chat_show() { SessionView* view = m_chat_folder.get_current_document(); if(view != NULL) m_actions.view_chat_userlist->set_enabled(true); } void Gobby::ViewCommands::on_chat_hide() { m_actions.view_chat_userlist->set_enabled(false); } void Gobby::ViewCommands::on_zoom_in() { Pango::FontDescription desc = m_preferences.appearance.font; desc.set_size(desc.get_size() * PANGO_SCALE_LARGE); m_preferences.appearance.font = desc; } void Gobby::ViewCommands::on_zoom_out() { Pango::FontDescription desc = m_preferences.appearance.font; desc.set_size(desc.get_size() / PANGO_SCALE_LARGE); m_preferences.appearance.font = desc; } void Gobby::ViewCommands::on_hide_user_colors() { SessionView* view = m_text_folder.get_current_document(); TextSessionView* text_view = dynamic_cast(view); g_assert(text_view != NULL); InfSession* session = INF_SESSION(text_view->get_session()); GtkTextBuffer* textbuffer = GTK_TEXT_BUFFER(text_view->get_text_buffer()); InfBuffer* buffer = inf_session_get_buffer(session); InfTextGtkBuffer* infbuffer = INF_TEXT_GTK_BUFFER(buffer); GtkTextIter start, end; gtk_text_buffer_get_start_iter(textbuffer, &start); gtk_text_buffer_get_end_iter(textbuffer, &end); inf_text_gtk_buffer_show_user_colors(infbuffer, FALSE, &start, &end); } void Gobby::ViewCommands::on_fullscreen_toggled() { bool is_fullscreen; m_actions.fullscreen->get_state(is_fullscreen); if(is_fullscreen) { if(!m_fullscreen.get()) m_fullscreen.reset(new Fullscreen(*this)); } else { m_fullscreen.reset(NULL); } } void Gobby::ViewCommands::on_menu_toolbar_toggled() { bool value; m_actions.view_toolbar->get_state(value); m_pref_view_toolbar_connection.block(); m_preferences.appearance.show_toolbar = value; m_pref_view_toolbar_connection.unblock(); } void Gobby::ViewCommands::on_menu_statusbar_toggled() { bool value; m_actions.view_statusbar->get_state(value); m_pref_view_statusbar_connection.block(); m_preferences.appearance.show_statusbar = value; m_pref_view_statusbar_connection.unblock(); } void Gobby::ViewCommands::on_menu_browser_toggled() { bool value; m_actions.view_browser->get_state(value); m_pref_view_browser_connection.block(); m_preferences.appearance.show_browser = value; m_pref_view_browser_connection.unblock(); } void Gobby::ViewCommands::on_menu_chat_toggled() { bool value; m_actions.view_chat->get_state(value); m_pref_view_chat_connection.block(); m_preferences.appearance.show_chat = value; m_pref_view_chat_connection.unblock(); } void Gobby::ViewCommands::on_menu_document_userlist_toggled() { bool value; m_actions.view_document_userlist->get_state(value); m_pref_view_document_userlist_connection.block(); m_preferences.appearance.show_document_userlist = value; m_pref_view_document_userlist_connection.unblock(); } void Gobby::ViewCommands::on_menu_chat_userlist_toggled() { bool value; m_actions.view_chat_userlist->get_state(value); m_pref_view_chat_userlist_connection.block(); m_preferences.appearance.show_chat_userlist = value; m_pref_view_chat_userlist_connection.unblock(); } void Gobby::ViewCommands::on_pref_toolbar_changed() { m_menu_view_toolbar_connection.block(); m_actions.view_toolbar->change_state( static_cast(m_preferences.appearance.show_toolbar)); m_menu_view_toolbar_connection.unblock(); } void Gobby::ViewCommands::on_pref_statusbar_changed() { m_menu_view_statusbar_connection.block(); m_actions.view_statusbar->change_state( static_cast(m_preferences.appearance.show_statusbar)); m_menu_view_statusbar_connection.unblock(); } void Gobby::ViewCommands::on_pref_browser_changed() { m_menu_view_browser_connection.block(); m_actions.view_browser->change_state( static_cast(m_preferences.appearance.show_browser)); m_menu_view_browser_connection.unblock(); } void Gobby::ViewCommands::on_pref_chat_changed() { m_menu_view_chat_connection.block(); m_actions.view_chat->change_state( static_cast(m_preferences.appearance.show_chat)); m_menu_view_chat_connection.unblock(); } void Gobby::ViewCommands::on_pref_document_userlist_changed() { m_menu_view_document_userlist_connection.block(); m_actions.view_document_userlist->change_state( static_cast( m_preferences.appearance.show_document_userlist)); m_menu_view_document_userlist_connection.unblock(); } void Gobby::ViewCommands::on_pref_chat_userlist_changed() { m_menu_view_chat_userlist_connection.block(); m_actions.view_chat_userlist->change_state( static_cast( m_preferences.appearance.show_chat_userlist)); m_menu_view_chat_userlist_connection.unblock(); } void Gobby::ViewCommands::on_menu_language_changed() { // TODO: Get ID, lookup language, set language. Glib::ustring language_id; m_actions.highlight_mode->get_state(language_id); GtkSourceLanguage* language = NULL; if(!language_id.empty()) { language = gtk_source_language_manager_get_language( m_language_manager, language_id.c_str()); // The language should exist by construction, if the languages // available in the language manager don't change at runtime g_assert(language != NULL); } g_assert(m_current_view != NULL); m_document_language_changed_connection.block(); m_current_view->set_language(language); m_document_language_changed_connection.unblock(); } void Gobby::ViewCommands::on_doc_language_changed(GtkSourceLanguage* language) { m_menu_language_changed_connection.block(); const gchar* language_id = ""; if(language != NULL) language_id = gtk_source_language_get_id(language); m_actions.highlight_mode->change_state( Glib::Variant::create(language_id)); m_menu_language_changed_connection.unblock(); } code/commands/view-commands.hpp000066400000000000000000000066011304552132100170520ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_VIEW_COMMANDS_HPP_ #define _GOBBY_VIEW_COMMANDS_HPP_ #include "core/folder.hpp" #include "core/closableframe.hpp" #include "core/windowactions.hpp" #include #include namespace Gobby { class ViewCommands: public sigc::trackable { private: class Fullscreen; public: ViewCommands(Gtk::Window& window, WindowActions& actions, GtkSourceLanguageManager* language_manager, const Folder& text_folder, ClosableFrame& chat_frame, const Folder& chat_folder, Preferences& preferences); ~ViewCommands(); protected: void on_text_document_changed(SessionView* view); void on_chat_document_added(SessionView& view); void on_chat_document_removed(SessionView& view); void on_chat_document_changed(SessionView* view); void on_chat_show(); void on_chat_hide(); void on_hide_user_colors(); void on_fullscreen_toggled(); void on_zoom_in(); void on_zoom_out(); void on_menu_toolbar_toggled(); void on_menu_statusbar_toggled(); void on_menu_browser_toggled(); void on_menu_chat_toggled(); void on_menu_document_userlist_toggled(); void on_menu_chat_userlist_toggled(); void on_pref_toolbar_changed(); void on_pref_statusbar_changed(); void on_pref_browser_changed(); void on_pref_chat_changed(); void on_pref_document_userlist_changed(); void on_pref_chat_userlist_changed(); void on_menu_language_changed(); void on_doc_language_changed(GtkSourceLanguage* language); Gtk::Window& m_parent; WindowActions& m_actions; GtkSourceLanguageManager* m_language_manager; const Folder& m_text_folder; ClosableFrame& m_chat_frame; const Folder& m_chat_folder; Preferences& m_preferences; TextSessionView* m_current_view; std::unique_ptr m_fullscreen; sigc::connection m_menu_language_changed_connection; sigc::connection m_document_language_changed_connection; sigc::connection m_menu_view_toolbar_connection; sigc::connection m_menu_view_statusbar_connection; sigc::connection m_menu_view_browser_connection; sigc::connection m_menu_view_chat_connection; sigc::connection m_menu_view_document_userlist_connection; sigc::connection m_menu_view_chat_userlist_connection; sigc::connection m_pref_view_statusbar_connection; sigc::connection m_pref_view_toolbar_connection; sigc::connection m_pref_view_browser_connection; sigc::connection m_pref_view_chat_connection; sigc::connection m_pref_view_document_userlist_connection; sigc::connection m_pref_view_chat_userlist_connection; private: void ensure_find_dialog(); }; } #endif // _GOBBY_VIEW_COMMANDS_HPP_ code/core/000077500000000000000000000000001304552132100127145ustar00rootroot00000000000000code/core/Makefile.am000066400000000000000000000040231304552132100147470ustar00rootroot00000000000000include code/core/gobject/Makefile.am gobby_0_5_SOURCES += \ code/core/applicationactions.cpp \ code/core/browser.cpp \ code/core/certificatemanager.cpp \ code/core/chatsessionview.cpp \ code/core/chattablabel.cpp \ code/core/closableframe.cpp \ code/core/connectionmanager.cpp \ code/core/credentialsgenerator.cpp \ code/core/documentinfostorage.cpp \ code/core/filechooser.cpp \ code/core/folder.cpp \ code/core/foldermanager.cpp \ code/core/huebutton.cpp \ code/core/knownhoststorage.cpp \ code/core/menumanager.cpp \ code/core/nodewatch.cpp \ code/core/noteplugin.cpp \ code/core/preferences.cpp \ code/core/selfhoster.cpp \ code/core/server.cpp \ code/core/sessionuserview.cpp \ code/core/sessionview.cpp \ code/core/statusbar.cpp \ code/core/tablabel.cpp \ code/core/textsessionuserview.cpp \ code/core/textsessionview.cpp \ code/core/textundogrouping.cpp \ code/core/texttablabel.cpp \ code/core/titlebar.cpp \ code/core/toolbar.cpp \ code/core/userjoin.cpp \ code/core/userlist.cpp \ code/core/windowactions.cpp noinst_HEADERS += \ code/core/applicationactions.hpp \ code/core/browser.hpp \ code/core/certificatemanager.hpp \ code/core/chatsessionview.hpp \ code/core/chattablabel.hpp \ code/core/closableframe.hpp \ code/core/connectionmanager.hpp \ code/core/credentialsgenerator.hpp \ code/core/documentinfostorage.hpp \ code/core/filechooser.hpp \ code/core/folder.hpp \ code/core/foldermanager.hpp \ code/core/menumanager.hpp \ code/core/huebutton.hpp \ code/core/knownhoststorage.hpp \ code/core/nodewatch.hpp \ code/core/noteplugin.hpp \ code/core/preferences.hpp \ code/core/selfhoster.hpp \ code/core/server.hpp \ code/core/sessionuserview.hpp \ code/core/sessionview.hpp \ code/core/statusbar.hpp \ code/core/tablabel.hpp \ code/core/textsessionuserview.hpp \ code/core/textsessionview.hpp \ code/core/textundogrouping.hpp \ code/core/texttablabel.hpp \ code/core/titlebar.hpp \ code/core/toolbar.hpp \ code/core/userjoin.hpp \ code/core/userlist.hpp \ code/core/windowactions.hpp code/core/applicationactions.cpp000066400000000000000000000021031304552132100173000ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "applicationactions.hpp" Gobby::ApplicationActions::ApplicationActions(Gio::ActionMap& map): quit(map.add_action("quit")), preferences(map.add_action("preferences")), help(map.add_action("help")), about(map.add_action("about")) { } code/core/applicationactions.hpp000066400000000000000000000024101304552132100173060ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_APPLICATIONACTIONS_HPP_ #define _GOBBY_APPLICATIONACTIONS_HPP_ #include namespace Gobby { class ApplicationActions { public: ApplicationActions(Gio::ActionMap& map); const Glib::RefPtr quit; const Glib::RefPtr preferences; const Glib::RefPtr help; const Glib::RefPtr about; }; } #endif // _GOBBY_APPLICATIONACTIONS_HPP_ code/core/browser.cpp000066400000000000000000000264571304552132100151210ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "dialogs/password-dialog.hpp" #include "core/browser.hpp" #include "core/noteplugin.hpp" #include "util/file.hpp" #include "util/uri.hpp" #include "util/i18n.hpp" #include #include #include #include gint compare_func(GtkTreeModel* model, GtkTreeIter* first, GtkTreeIter* second, gpointer user_data) { gint result; InfBrowser* br_one; InfBrowser* br_two; InfBrowserIter* bri_one; InfBrowserIter* bri_two; GtkTreeIter parent; result = 0; if(gtk_tree_model_iter_parent(model, &parent, first)) { g_assert(gtk_tree_model_iter_parent(model, &parent, second)); gtk_tree_model_get( model, first, INF_GTK_BROWSER_MODEL_COL_BROWSER, &br_one, INF_GTK_BROWSER_MODEL_COL_NODE, &bri_one, -1); gtk_tree_model_get( model, second, INF_GTK_BROWSER_MODEL_COL_BROWSER, &br_two, INF_GTK_BROWSER_MODEL_COL_NODE, &bri_two, -1); if(inf_browser_is_subdirectory(br_one, bri_one) && !inf_browser_is_subdirectory(br_two, bri_two)) { result = -1; } else if(!inf_browser_is_subdirectory(br_one, bri_one) && inf_browser_is_subdirectory(br_two, bri_two)) { result = 1; } g_object_unref(br_one); g_object_unref(br_two); inf_browser_iter_free(bri_one); inf_browser_iter_free(bri_two); } if(!result) { gchar* name_one; gchar* name_two; gtk_tree_model_get( model, first, INF_GTK_BROWSER_MODEL_COL_NAME, &name_one, -1); gtk_tree_model_get( model, second, INF_GTK_BROWSER_MODEL_COL_NAME, &name_two, -1); gchar* one = g_utf8_casefold(name_one, -1); gchar* two = g_utf8_casefold(name_two, -1); result = g_utf8_collate(one, two); g_free(name_one); g_free(name_two); g_free(one); g_free(two); } return result; } Gobby::Browser::Browser(Gtk::Window& parent, StatusBar& status_bar, ConnectionManager& connection_manager): m_parent(parent), m_status_bar(status_bar), m_connection_manager(connection_manager), m_expander(_("_Direct Connection"), true), m_label_hostname(_("Host Name:")), m_entry_hostname(config_filename("recent_hosts"), 5) { m_label_hostname.show(); m_entry_hostname.set_hexpand(true); m_entry_hostname.get_entry()->signal_activate().connect( sigc::mem_fun(*this, &Browser::on_hostname_activate)); m_entry_hostname.show(); m_grid.set_column_spacing(6); m_grid.attach(m_label_hostname, 0, 0, 1, 1); m_grid.attach(m_entry_hostname, 1, 0, 1, 1); m_grid.show(); m_expander.set_spacing(6); m_expander.add(m_grid); m_expander.show(); m_expander.property_expanded().signal_changed().connect( sigc::mem_fun(*this, &Browser::on_expanded_changed)); m_browser_store = inf_gtk_browser_store_new( connection_manager.get_io(), connection_manager.get_communication_manager()); m_sort_model = inf_gtk_browser_model_sort_new( INF_GTK_BROWSER_MODEL(m_browser_store)); gtk_tree_sortable_set_default_sort_func( GTK_TREE_SORTABLE(m_sort_model), compare_func, NULL, NULL); if(m_connection_manager.get_discovery() != NULL) { inf_gtk_browser_store_add_discovery( m_browser_store, m_connection_manager.get_discovery()); } Glib::ustring known_hosts_file = config_filename("known_hosts"); m_cert_checker = inf_gtk_certificate_manager_new( parent.gobj(), m_connection_manager.get_xmpp_manager(), known_hosts_file.c_str()); m_browser_view = INF_GTK_BROWSER_VIEW( inf_gtk_browser_view_new_with_model( INF_GTK_BROWSER_MODEL(m_sort_model))); gtk_widget_show(GTK_WIDGET(m_browser_view)); gtk_container_add(GTK_CONTAINER(m_scroll.gobj()), GTK_WIDGET(m_browser_view)); m_scroll.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); m_scroll.set_shadow_type(Gtk::SHADOW_IN); m_scroll.set_vexpand(true); m_scroll.show(); connection_manager.signal_connection_replaced().connect( sigc::mem_fun(*this, &Browser::on_connection_replaced)); g_signal_connect( m_browser_store, "set-browser", G_CALLBACK(&on_set_browser_static), this ); g_signal_connect( m_browser_view, "activate", G_CALLBACK(&on_activate_static), this ); set_row_spacing(6); attach(m_scroll, 0, 0, 1, 1); attach(m_expander, 0, 1, 1, 1); init_accessibility(); set_focus_child(m_expander); } Gobby::Browser::~Browser() { g_object_unref(m_browser_store); g_object_unref(m_sort_model); g_object_unref(m_cert_checker); } void Gobby::Browser::init_accessibility() { Glib::RefPtr relation_set; Glib::RefPtr relation; std::vector > targets; // Associate the hostname label with the corresponding entry field. Glib::RefPtr entry_hostname_acc = m_entry_hostname.get_accessible(); Glib::RefPtr label_hostname_acc = m_label_hostname.get_accessible(); relation_set = entry_hostname_acc->get_relation_set(); targets.push_back(label_hostname_acc); relation = Atk::Relation::create(targets, Atk::RELATION_LABELLED_BY); relation_set->set_add(relation); } bool Gobby::Browser::get_selected_browser(InfBrowser** browser) { GtkTreeIter tree_iter; if(!inf_gtk_browser_view_get_selected(m_browser_view, &tree_iter)) return false; InfBrowser* tmp_browser; gtk_tree_model_get( GTK_TREE_MODEL(m_sort_model), &tree_iter, INF_GTK_BROWSER_MODEL_COL_BROWSER, &tmp_browser, -1); if(tmp_browser == NULL) return false; *browser = tmp_browser; g_object_unref(tmp_browser); return true; } bool Gobby::Browser::get_selected_iter(InfBrowser* browser, InfBrowserIter* iter) { GtkTreeIter tree_iter; if(!inf_gtk_browser_view_get_selected(m_browser_view, &tree_iter)) return false; InfBrowserStatus browser_status; g_object_get(G_OBJECT(browser), "status", &browser_status, NULL); if(browser_status != INF_BROWSER_OPEN) return false; InfBrowserIter *tmp_iter; gtk_tree_model_get( GTK_TREE_MODEL(m_sort_model), &tree_iter, INF_GTK_BROWSER_MODEL_COL_NODE, &tmp_iter, -1); *iter = *tmp_iter; inf_browser_iter_free(tmp_iter); return true; } void Gobby::Browser::set_selected(InfBrowser* browser, const InfBrowserIter* iter) { GtkTreeIter tree_iter; gboolean has_iter = inf_gtk_browser_model_browser_iter_to_tree_iter( INF_GTK_BROWSER_MODEL(m_sort_model), browser, iter, &tree_iter); g_assert(has_iter == TRUE); inf_gtk_browser_view_set_selected(m_browser_view, &tree_iter); } InfBrowser* Gobby::Browser::add_remote(const std::string& hostname, const std::string& service, unsigned int device_index, bool connect) { // Check whether we do have such a connection already: InfXmppConnection* xmpp = m_connection_manager.make_connection( hostname, service, device_index, connect); // Should have thrown otherwise: g_assert(xmpp != NULL); // TODO: Remove erroneous entry with same name, if any, before // adding. InfBrowser* browser = inf_gtk_browser_store_add_connection( m_browser_store, INF_XML_CONNECTION(xmpp), hostname.c_str()); return browser; } InfBrowser* Gobby::Browser::add_remote(const InfIpAddress* address, guint port, unsigned int device_index, const std::string& hostname, bool connect) { // Check whether we do have such a connection already: InfXmppConnection* xmpp = m_connection_manager.make_connection( address, port, device_index, hostname, connect); // Should have thrown otherwise: g_assert(xmpp != NULL); // TODO: Remove erroneous entry with same name, if any, before // adding. InfBrowser* browser = inf_gtk_browser_store_add_connection( m_browser_store, INF_XML_CONNECTION(xmpp), hostname.c_str()); return browser; } void Gobby::Browser::add_browser(InfBrowser* browser, const char* name) { inf_gtk_browser_store_add_browser( m_browser_store, browser, name); } void Gobby::Browser::remove_browser(InfBrowser* browser) { m_connection_manager.remove_connection( INF_XMPP_CONNECTION( infc_browser_get_connection( INFC_BROWSER(browser)))); inf_gtk_browser_store_remove_browser( m_browser_store, browser); } void Gobby::Browser::on_connection_replaced(InfXmppConnection* connection, InfXmppConnection* by) { // Remove the browser for the replaced connection, and highlight the // one it was replaced with. inf_gtk_browser_store_remove_connection( m_browser_store, INF_XML_CONNECTION(connection)); /* The connection exists already, we just use this function to obtain * the browser. * TODO: There should be a * inf_gtk_browser_store_find_browser_for_connection() function */ InfBrowser* browser = inf_gtk_browser_store_add_connection( m_browser_store, INF_XML_CONNECTION(by), ""); set_selected(browser, NULL); } void Gobby::Browser::on_expanded_changed() { if(m_expander.get_expanded()) { if(m_entry_hostname.get_realized()) { m_entry_hostname.grab_focus(); } else { m_entry_hostname.signal_realize().connect( sigc::mem_fun(m_entry_hostname, &Gtk::Entry::grab_focus)); } } } void Gobby::Browser::on_set_browser(GtkTreeIter* iter, InfBrowser* old_browser, InfBrowser* new_browser) { if(new_browser) { if(INFC_IS_BROWSER(new_browser)) { InfcBrowser* browser = INFC_BROWSER(new_browser); infc_browser_add_plugin(browser, Plugins::C_TEXT); infc_browser_add_plugin(browser, Plugins::C_CHAT); } else if(INFD_IS_DIRECTORY(new_browser)) { InfdDirectory* directory = INFD_DIRECTORY(new_browser); infd_directory_add_plugin(directory, Plugins::D_TEXT); infd_directory_add_plugin(directory, Plugins::D_CHAT); } } } void Gobby::Browser::on_activate(GtkTreeIter* iter) { InfBrowser* browser; InfBrowserIter* browser_iter; gtk_tree_model_get(GTK_TREE_MODEL(m_sort_model), iter, INF_GTK_BROWSER_MODEL_COL_BROWSER, &browser, INF_GTK_BROWSER_MODEL_COL_NODE, &browser_iter, -1); m_signal_activate.emit(browser, browser_iter); inf_browser_iter_free(browser_iter); g_object_unref(browser); } void Gobby::Browser::on_hostname_activate() { Glib::ustring str = m_entry_hostname.get_entry()->get_text(); if(str.empty()) return; m_entry_hostname.commit(); m_entry_hostname.get_entry()->set_text(""); m_signal_connect.emit(str); } code/core/browser.hpp000066400000000000000000000105211304552132100151070ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_BROWSER_HPP_ #define _GOBBY_BROWSER_HPP_ #include "core/connectionmanager.hpp" #include "core/statusbar.hpp" #include "util/historyentry.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Gobby { class Browser: public Gtk::Grid { public: typedef sigc::signal SignalConnect; typedef sigc::signal SignalActivate; Browser(Gtk::Window& parent, StatusBar& status_bar, ConnectionManager& connection_manager); ~Browser(); ConnectionManager& get_connection_manager() { return m_connection_manager; } InfGtkBrowserModelSort* get_store() { return m_sort_model; } const InfGtkBrowserModelSort* get_store() const { return m_sort_model; } InfGtkBrowserView* get_view() { return m_browser_view; } bool get_selected_browser(InfBrowser** browser); bool get_selected_iter(InfBrowser* browser, InfBrowserIter* iter); void set_selected(InfBrowser* browser, const InfBrowserIter* iter); InfBrowser* add_remote(const std::string& hostname, const std::string& service, unsigned int device_index, bool connect); InfBrowser* add_remote(const InfIpAddress* address, guint port, unsigned int device_index, const std::string& hostname, bool connect); void add_browser(InfBrowser* browser, const char* name); void remove_browser(InfBrowser* browser); SignalActivate signal_activate() const { return m_signal_activate; } SignalConnect signal_connect() const { return m_signal_connect; } protected: void init_accessibility(); static void on_set_browser_static(InfGtkBrowserModel* model, GtkTreePath* path, GtkTreeIter* iter, InfBrowser* old_browser, InfBrowser* new_browser, gpointer user_data) { static_cast(user_data)->on_set_browser( iter, old_browser, new_browser); } static void on_activate_static(InfGtkBrowserView* view, GtkTreeIter* iter, gpointer user_data) { static_cast(user_data)->on_activate(iter); } void on_connection_replaced(InfXmppConnection* connection, InfXmppConnection* by); void on_expanded_changed(); void on_set_browser(GtkTreeIter* iter, InfBrowser* old_browser, InfBrowser* new_browser); void on_activate(GtkTreeIter* iter); void on_hostname_activate(); Gtk::Window& m_parent; StatusBar& m_status_bar; ConnectionManager& m_connection_manager; InfGtkCertificateManager* m_cert_checker; InfGtkBrowserStore* m_browser_store; InfGtkBrowserView* m_browser_view; Gtk::ScrolledWindow m_scroll; Gtk::Expander m_expander; Gtk::Grid m_grid; Gtk::Label m_label_hostname; HistoryComboBox m_entry_hostname; InfGtkBrowserModelSort* m_sort_model; SignalConnect m_signal_connect; SignalActivate m_signal_activate; }; } #endif // _GOBBY_BROWSER_HPP_ code/core/certificatemanager.cpp000066400000000000000000000304531304552132100172420ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "core/certificatemanager.hpp" #include "util/file.hpp" #include "util/i18n.hpp" #include #include Gobby::CertificateManager::CertificateManager(Preferences& preferences): m_preferences(preferences), m_dh_params(NULL), m_key(NULL), m_certificates(NULL), m_credentials(NULL), m_key_error(NULL), m_certificate_error(NULL), m_trust_error(NULL) { m_conn_key_file = m_preferences.security.key_file. signal_changed().connect(sigc::mem_fun( *this, &CertificateManager::on_key_file_changed)); m_conn_certificate_file = m_preferences.security.certificate_file. signal_changed().connect(sigc::mem_fun( *this, &CertificateManager::on_certificate_file_changed)); m_preferences.security.use_system_trust.signal_changed().connect( sigc::mem_fun( *this, &CertificateManager::on_trusted_cas_changed)); m_preferences.security.trusted_cas.signal_changed().connect( sigc::mem_fun( *this, &CertificateManager::on_trusted_cas_changed)); m_preferences.security.authentication_enabled. signal_changed().connect( sigc::mem_fun( *this, &CertificateManager:: on_authentication_enabled_changed)); // TODO: Load these only on request, to improve the startup time load_dh_params(); load_key(); load_certificate(); load_trust(); make_credentials(); } Gobby::CertificateManager::~CertificateManager() { if(m_credentials != NULL) inf_certificate_credentials_unref(m_credentials); for(unsigned int i = 0; i < m_trust.size(); ++i) gnutls_x509_crt_deinit(m_trust[i]); if(m_certificates != NULL) inf_certificate_chain_unref(m_certificates); if(m_key != NULL) gnutls_x509_privkey_deinit(m_key); if(m_dh_params != NULL) gnutls_dh_params_deinit(m_dh_params); } void Gobby::CertificateManager::set_dh_params(gnutls_dh_params_t dh_params) { gnutls_dh_params_t old_dh_params = m_dh_params; GError* error = NULL; std::string filename = config_filename("dh_params.pem"); inf_cert_util_write_dh_params(dh_params, filename.c_str(), &error); if(error != NULL) { g_warning( _("Failed to write Diffie-Hellman parameters " "to \"%s\": %s"), filename.c_str(), error->message); g_error_free(error); } m_dh_params = dh_params; make_credentials(); // TODO: Note that the credentials do only store a pointer to the // DH params, so we cannot just delete the DH params here, since the // old credentials might still be in use. // For the moment we don't let this happen -- in principle it should // not happen; once we have valid DH params at one point we don't // need to change them again. // For the future maybe it could make sense to store the DH params // in the InfCertificateCredentials struct, so that their lifetime // is coupled. g_assert(old_dh_params == NULL); } void Gobby::CertificateManager::set_private_key(gnutls_x509_privkey_t key, const GError* error) { g_assert(key == NULL || error == NULL); gnutls_x509_privkey_t old_key = m_key; InfCertificateChain* old_certificates = m_certificates; if(old_certificates != NULL) inf_certificate_chain_ref(old_certificates); m_key = key; if(m_key_error != NULL) g_error_free(m_key_error); if(error != NULL) m_key_error = g_error_copy(error); else m_key_error = NULL; // Attempt to re-load the certificate if there was an error -- maybe // the new key fixes the problem. This makes sure that if the new key // is compatible to the certificate, the certificate is loaded. // TODO: It would be nicer to still keep the certificate in memory // when it does not match the key, so we don't need to re-load it. // Basically we just need to be able to handle the case when both // cert_error and certificate itself are non-NULL. if(m_certificate_error != NULL) { load_certificate(); } else { check_certificate_signature(); make_credentials(); } // Note that this relies on the fact that // gnutls_certificate_set_x509_key makes a copy of the key // and certificate if(old_certificates != NULL) inf_certificate_chain_unref(old_certificates); if(old_key != NULL) gnutls_x509_privkey_deinit(old_key); } void Gobby::CertificateManager::set_private_key(gnutls_x509_privkey_t key, const char* filename, const GError* error) { if(error != NULL) { g_assert(key == NULL); set_private_key(NULL, error); } else { GError* local_error = NULL; if(filename != NULL) { m_conn_key_file.block(); m_preferences.security.key_file = filename; m_conn_key_file.unblock(); if(key != NULL) { inf_cert_util_write_private_key( key, filename, &local_error); } } if(local_error != NULL) { set_private_key(NULL, local_error); if(key != NULL) gnutls_x509_privkey_deinit(key); g_error_free(local_error); } else { set_private_key(key, NULL); } } } void Gobby::CertificateManager::set_certificates(gnutls_x509_crt_t* certs, guint n_certs, const GError* error) { g_assert(n_certs == 0 || error == NULL); InfCertificateChain* old_certificates = m_certificates; m_certificates = NULL; if(n_certs > 0) m_certificates = inf_certificate_chain_new(certs, n_certs); else m_certificates = NULL; if(m_certificate_error != NULL) g_error_free(m_certificate_error); if(error != NULL) m_certificate_error = g_error_copy(error); else m_certificate_error = NULL; check_certificate_signature(); make_credentials(); // Note that this relies on the fact that // gnutls_certificate_set_x509_key makes a copy of the certificates if(old_certificates != NULL) inf_certificate_chain_unref(old_certificates); } void Gobby::CertificateManager::set_certificates(gnutls_x509_crt_t* certs, guint n_certs, const char* filename, const GError* error) { if(error != NULL) { g_assert(n_certs == 0); set_certificates(NULL, 0, error); } else { GError* local_error = NULL; if(filename != NULL) { m_conn_certificate_file.block(); m_preferences.security.certificate_file = filename; m_conn_certificate_file.unblock(); if(n_certs > 0) { inf_cert_util_write_certificate( certs, n_certs, filename, &local_error); } } if(local_error != NULL) { set_certificates(NULL, 0, local_error); for(guint i = 0; i < n_certs; ++i) gnutls_x509_crt_deinit(certs[i]); g_error_free(local_error); } else { set_certificates(certs, n_certs, NULL); } } } void Gobby::CertificateManager::load_dh_params() { const std::string filename = config_filename("dh_params.pem"); GError* error = NULL; gnutls_dh_params_t dh_params = inf_cert_util_read_dh_params(filename.c_str(), &error); if(error != NULL) { if(error->domain != G_FILE_ERROR || error->code != G_FILE_ERROR_NOENT) { g_warning(_("Failed to read Diffie-Hellman " "parameters: %s"), error->message); } g_error_free(error); } if(dh_params != NULL) set_dh_params(dh_params); } void Gobby::CertificateManager::load_key() { const std::string& filename = m_preferences.security.key_file; if(!filename.empty()) { GError* error = NULL; gnutls_x509_privkey_t key = inf_cert_util_read_private_key( filename.c_str(), &error); set_private_key(key, error); if(error != NULL) g_error_free(error); } else { set_private_key(NULL, NULL); } } void Gobby::CertificateManager::load_certificate() { const std::string& filename = m_preferences.security.certificate_file; if(!filename.empty()) { GError* error = NULL; GPtrArray* array = inf_cert_util_read_certificate( filename.c_str(), NULL, &error); if(array != NULL) { g_assert(error == NULL); guint n_certs = array->len; gnutls_x509_crt_t* certs = reinterpret_cast( g_ptr_array_free(array, FALSE)); if(n_certs > 0) { set_certificates(certs, n_certs, NULL); } else { g_set_error( &error, g_quark_from_static_string( "GOBBY_CERTIFICATE_MANAGER_" "ERROR"), 1, "%s", _("File does not contain a " "X.509 certificate") ); set_certificates(NULL, 0, error); g_error_free(error); g_free(certs); } } else { g_assert(error != NULL); set_certificates(NULL, 0, error); g_error_free(error); } } else { set_certificates(NULL, 0, NULL); } } void Gobby::CertificateManager::load_trust() { std::vector old_trust; old_trust.swap(m_trust); GError* error = NULL; const std::string& filename = m_preferences.security.trusted_cas; if(!filename.empty()) { GPtrArray* array = inf_cert_util_read_certificate( filename.c_str(), NULL, &error); if(array != NULL) { guint n_certs = array->len; gnutls_x509_crt_t* certs = reinterpret_cast( g_ptr_array_free(array, FALSE)); m_trust.assign(certs, certs + n_certs); g_free(certs); } } if(m_trust_error != NULL) g_error_free(m_trust_error); m_trust_error = error; make_credentials(); // Note that this relies on the fact that // gnutls_certificate_set_x509_trust makes a copy of the certificates for(unsigned int i = 0; i < old_trust.size(); ++i) gnutls_x509_crt_deinit(old_trust[i]); } void Gobby::CertificateManager::check_certificate_signature() { if(!m_key || !m_certificates) return; g_assert(m_key_error == NULL && m_certificate_error == NULL); gnutls_x509_crt_t crt = inf_certificate_chain_get_own_certificate(m_certificates); if(!inf_cert_util_check_certificate_key(crt, m_key)) { inf_certificate_chain_unref(m_certificates); m_certificates = NULL; g_set_error( &m_certificate_error, g_quark_from_static_string( "GOBBY_CERTIFICATE_MANAGER_ERROR"), 0, "%s", _("Certificate does not belong to the chosen key") ); } } void Gobby::CertificateManager::make_credentials() { InfCertificateCredentials* creds = inf_certificate_credentials_new(); gnutls_certificate_credentials_t gnutls_creds = inf_certificate_credentials_get(creds); if(m_preferences.security.authentication_enabled && m_key != NULL && m_certificates != NULL) { gnutls_certificate_set_x509_key( gnutls_creds, inf_certificate_chain_get_raw(m_certificates), inf_certificate_chain_get_n_certificates(m_certificates), m_key ); } if(m_preferences.security.use_system_trust) { const int n_cas = gnutls_certificate_set_x509_system_trust( gnutls_creds); if(n_cas < 0) { g_warning("Failed to add system CAs: %s\n", gnutls_strerror(n_cas)); } } if(!m_trust.empty()) { gnutls_certificate_set_x509_trust( gnutls_creds, &m_trust[0], m_trust.size() ); } if(m_dh_params != NULL) gnutls_certificate_set_dh_params(gnutls_creds, m_dh_params); gnutls_certificate_set_verify_flags( gnutls_creds, GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT); InfCertificateCredentials* old_creds = m_credentials; m_credentials = creds; m_signal_credentials_changed.emit(); if(old_creds != NULL) inf_certificate_credentials_unref(old_creds); } void Gobby::CertificateManager::on_key_file_changed() { load_key(); //make_credentials(); } void Gobby::CertificateManager::on_certificate_file_changed() { load_certificate(); //make_credentials(); } void Gobby::CertificateManager::on_trusted_cas_changed() { load_trust(); //make_credentials(); } void Gobby::CertificateManager::on_authentication_enabled_changed() { make_credentials(); } code/core/certificatemanager.hpp000066400000000000000000000066131304552132100172500ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_CERTIFICATEMANAGER_HPP_ #define _GOBBY_CERTIFICATEMANAGER_HPP_ #include "core/preferences.hpp" namespace Gobby { class CertificateManager { public: CertificateManager(Preferences& preferences); ~CertificateManager(); gnutls_dh_params_t get_dh_params() const { return m_dh_params; } gnutls_x509_privkey_t get_private_key() const { return m_key; } InfCertificateChain* get_certificates() const { return m_certificates; } void set_dh_params(gnutls_dh_params_t dh_params); void set_private_key(gnutls_x509_privkey_t key, const GError* error); void set_private_key(gnutls_x509_privkey_t key, const char* filename, const GError* error); void set_certificates(gnutls_x509_crt_t* certs, guint n_certs, const GError* error); void set_certificates(gnutls_x509_crt_t* certs, guint n_certs, const char* filename, const GError* error); unsigned int get_n_trusted_cas() const { return m_trust.size(); } const gnutls_x509_crt_t* get_trusted_cas() const { return m_trust.empty() ? NULL : &m_trust[0]; } InfCertificateCredentials* get_credentials() const { return m_credentials; } // If an error occurs with the loading, the error can be // queried here. const GError* get_key_error() const { return m_key_error; } const GError* get_certificate_error() const { return m_certificate_error; } const GError* get_trust_error() const { return m_trust_error; } typedef sigc::signal signal_credentials_changed_type; signal_credentials_changed_type signal_credentials_changed() const { return m_signal_credentials_changed; } protected: Preferences& m_preferences; sigc::connection m_conn_key_file; sigc::connection m_conn_certificate_file; gnutls_dh_params_t m_dh_params; gnutls_x509_privkey_t m_key; InfCertificateChain* m_certificates; std::vector m_trust; InfCertificateCredentials* m_credentials; GError* m_key_error; GError* m_certificate_error; GError* m_trust_error; signal_credentials_changed_type m_signal_credentials_changed; private: void load_dh_params(); void load_key(); void load_certificate(); void load_trust(); void check_certificate_signature(); void make_credentials(); void on_key_file_changed(); void on_certificate_file_changed(); void on_trusted_cas_changed(); void on_authentication_enabled_changed(); }; } #endif // _GOBBY_CERTIFICATEMANAGER_HPP_ code/core/chatsessionview.cpp000066400000000000000000000037561304552132100166510ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "core/chatsessionview.hpp" Gobby::ChatSessionView::ChatSessionView(InfChatSession* session, const Glib::ustring& title, const Glib::ustring& path, const Glib::ustring& hostname, Preferences& preferences): SessionView(INF_SESSION(session), title, path, hostname), m_preferences(preferences), m_chat(INF_GTK_CHAT(inf_gtk_chat_new())) { inf_gtk_chat_set_session(m_chat, session); gtk_widget_show(GTK_WIDGET(m_chat)); gtk_grid_attach_next_to(GTK_GRID(gobj()), GTK_WIDGET(m_chat), GTK_WIDGET(m_info_frame.gobj()), GTK_POS_BOTTOM, 1, 1); } InfUser* Gobby::ChatSessionView::get_active_user() const { return inf_gtk_chat_get_active_user(m_chat); } void Gobby::ChatSessionView::set_active_user(InfUser* user) { g_assert( user == NULL || inf_user_table_lookup_user_by_id( inf_session_get_user_table(INF_SESSION(m_session)), inf_user_get_id(INF_USER(user))) == INF_USER(user)); inf_gtk_chat_set_active_user(m_chat, user); active_user_changed(user); } code/core/chatsessionview.hpp000066400000000000000000000032271304552132100166470ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_CHATSESSIONVIEW_HPP_ #define _GOBBY_CHATSESSIONVIEW_HPP_ #include "core/sessionview.hpp" #include "core/preferences.hpp" #include #include namespace Gobby { class ChatSessionView: public SessionView { public: ChatSessionView(InfChatSession* session, const Glib::ustring& title, const Glib::ustring& path, const Glib::ustring& hostname, Preferences& preferences); // Override base class covariantly InfChatSession* get_session() { return INF_CHAT_SESSION(m_session); } InfGtkChat* get_chat() { return m_chat; } virtual InfUser* get_active_user() const; void set_active_user(InfUser* user); protected: Preferences& m_preferences; InfGtkChat* m_chat; }; } #endif // _GOBBY_CHATSESSIONVIEW_HPP_ code/core/chattablabel.cpp000066400000000000000000000042171304552132100160320ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "core/chattablabel.hpp" Gobby::ChatTabLabel::ChatTabLabel(Folder& folder, ChatSessionView& view, bool always_show_close_button): TabLabel(folder, view, always_show_close_button ? "chat" : "network-idle"), m_always_show_close_button(always_show_close_button) { if(!m_always_show_close_button) m_button.hide(); // Only show when disconnected InfChatBuffer* buffer = INF_CHAT_BUFFER( inf_session_get_buffer(INF_SESSION(view.get_session()))); m_add_message_handle = g_signal_connect_after( G_OBJECT(buffer), "add-message", G_CALLBACK(on_add_message_static), this); } Gobby::ChatTabLabel::~ChatTabLabel() { InfChatBuffer* buffer = INF_CHAT_BUFFER( inf_session_get_buffer(INF_SESSION(m_view.get_session()))); g_signal_handler_disconnect(buffer, m_add_message_handle); } void Gobby::ChatTabLabel::on_notify_subscription_group() { InfSession* session = INF_SESSION(m_view.get_session()); if(inf_session_get_subscription_group(session) != NULL && !m_always_show_close_button) { m_button.hide(); } else { m_button.show(); } } void Gobby::ChatTabLabel::on_changed(InfUser* author) { if(!m_changed) { InfSession* session = INF_SESSION(m_view.get_session()); if(inf_session_get_status(session) == INF_SESSION_RUNNING) set_changed(); } } code/core/chattablabel.hpp000066400000000000000000000031561304552132100160400ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_CHATTABLABEL_HPP_ #define _GOBBY_CHATTABLABEL_HPP_ #include "core/chatsessionview.hpp" #include "core/tablabel.hpp" namespace Gobby { class ChatTabLabel: public TabLabel { public: ChatTabLabel(Folder& folder, ChatSessionView& view, bool always_show_close_button); ~ChatTabLabel(); protected: static void on_add_message_static(InfChatBuffer* buffer, InfChatBufferMessage* message, gpointer user_data) { static_cast(user_data)-> on_changed(message->user); } virtual void on_notify_subscription_group(); // override void on_changed(InfUser* author); private: const bool m_always_show_close_button; gulong m_add_message_handle; }; } #endif // _GOBBY_CHATTABLABEL_HPP_ code/core/closableframe.cpp000066400000000000000000000046751304552132100162330ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "core/closableframe.hpp" #include "util/closebutton.hpp" #include #include Gobby::ClosableFrame::ClosableFrame(const Glib::ustring& title, const Glib::ustring& icon_name, Preferences::Option& option): m_option(option), m_allow_visible(true) { CloseButton* button = Gtk::manage(new CloseButton); button->set_hexpand(true); button->set_halign(Gtk::ALIGN_END); button->signal_clicked().connect( sigc::mem_fun(*this, &ClosableFrame::on_clicked)); m_option.signal_changed().connect( sigc::mem_fun(*this, &ClosableFrame::on_option)); button->show(); Gtk::Image* image = Gtk::manage( new Gtk::Image); image->set_from_icon_name(icon_name, Gtk::ICON_SIZE_MENU); image->show(); Gtk::Label* label_title = Gtk::manage( new Gtk::Label(title, Gtk::ALIGN_START)); label_title->show(); m_grid.set_border_width(6); m_grid.set_column_spacing(6); m_grid.set_row_spacing(6); m_grid.attach(*image, 0, 0, 1, 1); m_grid.attach(*label_title, 1, 0, 1, 1); m_grid.attach(*button, 2, 0, 1, 1); m_grid.show(); add(m_grid); on_option(); } void Gobby::ClosableFrame::set_allow_visible(bool allow_visible) { m_allow_visible = allow_visible; if(m_option && m_allow_visible) show(); else hide(); } void Gobby::ClosableFrame::on_add(Gtk::Widget* widget) { if(widget == &m_grid) Gtk::Frame::on_add(widget); else m_grid.attach(*widget, 0, 1, 3, 1); } void Gobby::ClosableFrame::on_clicked() { m_option = false; } void Gobby::ClosableFrame::on_option() { if(m_option && m_allow_visible) show(); else hide(); } code/core/closableframe.hpp000066400000000000000000000027031304552132100162260ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_CLOSABLE_FRAME_HPP_ #define _GOBBY_CLOSABLE_FRAME_HPP_ #include "core/preferences.hpp" #include #include namespace Gobby { class ClosableFrame: public Gtk::Frame { public: ClosableFrame(const Glib::ustring& title, const Glib::ustring& icon_name, Preferences::Option& option); void set_allow_visible(bool allow_visible); protected: virtual void on_add(Gtk::Widget* widget); void on_clicked(); void on_option(); Preferences::Option& m_option; Gtk::Grid m_grid; bool m_allow_visible; }; } #endif // _GOBBY_CLOSABLE_FRAME_HPP_ code/core/connectionmanager.cpp000066400000000000000000000252471304552132100171240ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "core/connectionmanager.hpp" #include Gobby::ConnectionManager::ConnectionManager(const CertificateManager& manager, const Preferences& preferences): m_cert_manager(manager), m_preferences(preferences), m_io(INF_IO(inf_gtk_io_new())), m_communication_manager(inf_communication_manager_new()), m_xmpp_manager(inf_xmpp_manager_new()), m_sasl_context(NULL) { m_connection_added_handler = g_signal_connect_after( G_OBJECT(m_xmpp_manager), "connection-added", G_CALLBACK(on_connection_added_static), this); m_connection_removed_handler = g_signal_connect_after( G_OBJECT(m_xmpp_manager), "connection-removed", G_CALLBACK(on_connection_removed_static), this); #ifdef LIBINFINITY_HAVE_AVAHI m_discovery = inf_discovery_avahi_new(m_io, m_xmpp_manager, m_cert_manager.get_credentials(), NULL, NULL); // TODO: Should be a constructor argument... inf_discovery_avahi_set_security_policy( m_discovery, m_preferences.security.policy); const InfKeepalive& keepalive = m_preferences.network.keepalive; inf_discovery_avahi_set_keepalive(m_discovery, &keepalive); #endif m_preferences.security.policy.signal_changed().connect( sigc::mem_fun( *this, &ConnectionManager::on_security_policy_changed)); m_preferences.network.keepalive.signal_changed().connect( sigc::mem_fun( *this, &ConnectionManager::on_keepalive_changed)); m_cert_manager.signal_credentials_changed().connect( sigc::mem_fun( *this, &ConnectionManager::on_credentials_changed)); } Gobby::ConnectionManager::~ConnectionManager() { #ifdef LIBINFINITY_HAVE_AVAHI g_object_unref(m_discovery); #endif if(m_sasl_context) inf_sasl_context_unref(m_sasl_context); for(std::map::const_iterator it = m_connections.begin(); it != m_connections.end(); ++it) { g_signal_handler_disconnect( G_OBJECT(it->first), it->second.notify_status_handler); } g_signal_handler_disconnect(G_OBJECT(m_xmpp_manager), m_connection_added_handler); g_signal_handler_disconnect(G_OBJECT(m_xmpp_manager), m_connection_removed_handler); g_object_unref(m_xmpp_manager); g_object_unref(m_communication_manager); g_object_unref(m_io); } InfXmppConnection* Gobby::ConnectionManager::make_connection( const std::string& hostname, const std::string& service, unsigned int device_index, bool connect) { // Check whether we do have such a connection already: InfXmppConnection* xmpp = inf_xmpp_manager_lookup_connection_by_hostname( m_xmpp_manager, hostname.c_str(), service.c_str(), "_infinote._tcp"); if(!xmpp) { InfNameResolver* resolver = inf_name_resolver_new( m_io, hostname.c_str(), service.c_str(), "_infinote._tcp"); InfTcpConnection* connection = inf_tcp_connection_new_resolve( m_io, resolver); g_object_unref(resolver); xmpp = create_connection( connection, device_index, hostname, connect); } else if(connect) { InfXmlConnectionStatus status; g_object_get(G_OBJECT(xmpp), "status", &status, NULL); if(status == INF_XML_CONNECTION_CLOSED) { GError* error = NULL; inf_xml_connection_open(INF_XML_CONNECTION(xmpp), &error); if(error != NULL) { std::string message = error->message; g_error_free(error); throw std::runtime_error(message); } } } g_assert(xmpp != NULL); return xmpp; } InfXmppConnection* Gobby::ConnectionManager::make_connection( const InfIpAddress* address, guint port, unsigned int device_index, const std::string& hostname, bool connect) { // Check whether we do have such a connection already: InfXmppConnection* xmpp = inf_xmpp_manager_lookup_connection_by_address( m_xmpp_manager, address, port); if(!xmpp) { InfTcpConnection* connection = inf_tcp_connection_new( m_io, address, port); xmpp = create_connection( connection, device_index, hostname, connect); } else if(connect) { InfXmlConnectionStatus status; g_object_get(G_OBJECT(xmpp), "status", &status, NULL); if(status == INF_XML_CONNECTION_CLOSED) { GError* error = NULL; inf_xml_connection_open(INF_XML_CONNECTION(xmpp), &error); if(error != NULL) { std::string message = error->message; g_error_free(error); throw std::runtime_error(message); } } } g_assert(xmpp != NULL); return xmpp; } InfXmppConnection* Gobby::ConnectionManager::create_connection(InfTcpConnection* connection, unsigned int device_index, const std::string& hostname, bool connect) { g_object_set(G_OBJECT(connection), "device-index", device_index, NULL); const InfKeepalive& keepalive = m_preferences.network.keepalive; GError* error = NULL; if(!inf_tcp_connection_set_keepalive(connection, &keepalive, &error)) { /* This should not happen, since set_keepalive can only fail * if the connection is open already. */ g_warning("Failed to set keepalive: %s", error->message); g_error_free(error); error = NULL; } if(connect && !inf_tcp_connection_open(connection, &error)) { std::string message = error->message; g_error_free(error); g_object_unref(connection); throw std::runtime_error(message); } else { InfXmppConnection* xmpp = inf_xmpp_connection_new( connection, INF_XMPP_CONNECTION_CLIENT, NULL, hostname.c_str(), m_preferences.security.policy, m_cert_manager.get_credentials(), m_sasl_context, m_sasl_mechanisms.empty() ? "" : m_sasl_mechanisms.c_str()); inf_xmpp_manager_add_connection(m_xmpp_manager, xmpp); g_object_unref(xmpp); g_object_unref(connection); return xmpp; } } void Gobby::ConnectionManager::remove_connection(InfXmppConnection* connection) { inf_xmpp_manager_remove_connection( m_xmpp_manager, connection); } void Gobby::ConnectionManager::set_sasl_context(InfSaslContext* sasl_context, const char* mechanisms) { if(m_sasl_context) inf_sasl_context_unref(m_sasl_context); m_sasl_context = sasl_context; if(m_sasl_context) inf_sasl_context_ref(m_sasl_context); m_sasl_mechanisms = mechanisms ? mechanisms : ""; for(std::map::const_iterator it = m_connections.begin(); it != m_connections.end(); ++it) { InfXmppConnection* conn = it->first; inf_xmpp_connection_reset_sasl_authentication( conn, m_sasl_context, m_sasl_mechanisms.empty() ? "" : m_sasl_mechanisms.c_str()); } #ifdef LIBINFINITY_HAVE_AVAHI g_object_set(G_OBJECT(m_discovery), "sasl-context", m_sasl_context, "sasl-mechanisms", mechanisms, NULL); #endif } void Gobby::ConnectionManager::on_connection_added(InfXmppConnection* xmpp) { g_assert(m_connections.find(xmpp) == m_connections.end()); ConnectionInfo info; info.notify_status_handler = g_signal_connect( G_OBJECT(xmpp), "notify::status", G_CALLBACK(on_notify_status_static), this); m_connections[xmpp] = info; } void Gobby::ConnectionManager::on_connection_removed( InfXmppConnection* xmpp, InfXmppConnection* replaced_by) { std::map::iterator iter = m_connections.find(xmpp); g_assert(iter != m_connections.end()); ConnectionInfo& info = iter->second; g_signal_handler_disconnect(G_OBJECT(xmpp), info.notify_status_handler); m_connections.erase(iter); if(replaced_by != NULL) { m_signal_connection_replaced.emit(xmpp, replaced_by); // This is not needed, since the connection will be // removed anyway, because nobody holds a reference // anymore. If it turns out that the connection stays // alive, and for example the certificate dialog shows // up, then it means we have a memory leak somewhere. //inf_xml_connection_close(INF_XML_CONNECTION(xmpp)); } } void Gobby::ConnectionManager::on_security_policy_changed() { #ifdef LIBINFINITY_HAVE_AVAHI inf_discovery_avahi_set_security_policy( m_discovery, m_preferences.security.policy); #endif } void Gobby::ConnectionManager::on_keepalive_changed() { const InfKeepalive& keepalive = m_preferences.network.keepalive; #ifdef LIBINFINITY_HAVE_AVAHI inf_discovery_avahi_set_keepalive(m_discovery, &keepalive); #endif for(std::map::const_iterator it = m_connections.begin(); it != m_connections.end(); ++it) { InfTcpConnection* tcp; g_object_get(G_OBJECT(it->first), "tcp-connection", &tcp, NULL); GError* error = NULL; inf_tcp_connection_set_keepalive(tcp, &keepalive, &error); g_object_unref(tcp); if(error != NULL) { g_warning("Failed to set keepalive: %s", error->message); g_error_free(error); } } } void Gobby::ConnectionManager::on_credentials_changed() { // Keep existing connections with current credentials but set // new credentials for all closed connections. for(std::map::const_iterator it = m_connections.begin(); it != m_connections.end(); ++it) { InfXmlConnectionStatus status; g_object_get(G_OBJECT(it->first), "status", &status, NULL); if(status == INF_XML_CONNECTION_CLOSED) { g_object_set( G_OBJECT(it->first), "credentials", m_cert_manager.get_credentials(), NULL); } } #ifdef LIBINFINITY_HAVE_AVAHI g_object_set( G_OBJECT(m_discovery), "credentials", m_cert_manager.get_credentials(), NULL); #endif } void Gobby::ConnectionManager::on_notify_status(InfXmppConnection* connection) { InfXmlConnectionStatus status; g_object_get(G_OBJECT(connection), "status", &status, NULL); // When the connection was closed, update the certificate credentials, // so that in case it is reopened the current credentials are used. if(status == INF_XML_CONNECTION_CLOSED) { g_object_set( G_OBJECT(connection), "credentials", m_cert_manager.get_credentials(), NULL); } } code/core/connectionmanager.hpp000066400000000000000000000123561304552132100171260ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_CONNECTIONMANAGER_HPP_ #define _GOBBY_CONNECTIONMANAGER_HPP_ #include "core/preferences.hpp" #include "core/certificatemanager.hpp" #include #include #include #include #include #include namespace Gobby { // This class manages outgoing connections. It also manages an // InfDiscoveryAvahi object. Incoming connections are handled by // SelfHoster. class ConnectionManager: public sigc::trackable { public: typedef sigc::signal SignalConnectionReplaced; ConnectionManager(const CertificateManager& cert_manager, const Preferences& preferences); ~ConnectionManager(); InfIo* get_io() { return m_io; } InfCommunicationManager* get_communication_manager() { return m_communication_manager; } InfXmppManager* get_xmpp_manager() { return m_xmpp_manager; } #ifdef LIBINFINITY_HAVE_AVAHI InfDiscovery* get_discovery() { return INF_DISCOVERY(m_discovery); } InfLocalPublisher* get_publisher() { return INF_LOCAL_PUBLISHER(m_discovery); } #else InfDiscovery* get_discovery() { return NULL; } InfLocalPublisher* get_publisher() { return NULL; } #endif // Use existing connection if any, otherwise make new one. May throw. // If connect is false and a new connection would need to be made, // then don't immediately open the new connection. InfXmppConnection* make_connection(const std::string& hostname, const std::string& service, unsigned int device_index, bool connect); InfXmppConnection* make_connection(const InfIpAddress* address, guint port, unsigned int device_index, const std::string& hostname, bool connect); void remove_connection(InfXmppConnection* connection); // SASL context to be used for all new connections void set_sasl_context(InfSaslContext* sasl_context, const char* mechanisms); SignalConnectionReplaced signal_connection_replaced() const { return m_signal_connection_replaced; } private: InfXmppConnection* create_connection(InfTcpConnection* connection, unsigned int device_index, const std::string& hostname, bool connect); static void on_notify_status_static(GObject* object, GParamSpec* pspec, gpointer user_data) { static_cast(user_data)->on_notify_status( INF_XMPP_CONNECTION(object)); } static void on_connection_added_static(InfXmppManager* manager, InfXmppConnection* xmpp, gpointer user_data) { static_cast(user_data)-> on_connection_added(xmpp); } static void on_connection_removed_static(InfXmppManager* manager, InfXmppConnection* xmpp, InfXmppConnection* replaced, gpointer user_data) { static_cast(user_data)-> on_connection_removed(xmpp, replaced); } protected: void on_connection_added(InfXmppConnection* xmpp); void on_connection_removed(InfXmppConnection* xmpp, InfXmppConnection* replaced_by); void on_security_policy_changed(); void on_keepalive_changed(); void on_credentials_changed(); void on_notify_status(InfXmppConnection* connection); const CertificateManager& m_cert_manager; const Preferences& m_preferences; InfIo* m_io; InfCommunicationManager* m_communication_manager; InfXmppManager* m_xmpp_manager; struct ConnectionInfo { gulong notify_status_handler; }; std::map m_connections; gulong m_connection_added_handler; gulong m_connection_removed_handler; InfSaslContext* m_sasl_context; std::string m_sasl_mechanisms; #ifdef LIBINFINITY_HAVE_AVAHI InfDiscoveryAvahi* m_discovery; #endif SignalConnectionReplaced m_signal_connection_replaced; }; } #endif // _GOBBY_CONNECTIONMANAGER_HPP_ code/core/credentialsgenerator.cpp000066400000000000000000000116171304552132100176320ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "core/credentialsgenerator.hpp" #include #include #include namespace { class Keygen: public Gobby::AsyncOperation { public: Keygen(gnutls_pk_algorithm_t algo, unsigned int bits, const Gobby::SlotKeyGeneratorDone& slot_done): m_algo(algo), m_bits(bits), m_slot_done(slot_done), m_key(NULL), m_error(NULL) { } ~Keygen() { if(m_key != NULL) gnutls_x509_privkey_deinit(m_key); if(m_error != NULL) g_error_free(m_error); } protected: virtual void run() { m_key = inf_cert_util_create_private_key( m_algo, m_bits, &m_error); } virtual void finish() { if(m_error != NULL) { m_slot_done(get_handle(), NULL, m_error); } else { gnutls_x509_privkey_t key = m_key; m_key = NULL; m_slot_done(get_handle(), key, NULL); } } private: const gnutls_pk_algorithm_t m_algo; const unsigned int m_bits; const Gobby::SlotKeyGeneratorDone m_slot_done; gnutls_x509_privkey_t m_key; GError* m_error; }; class Certgen: public Gobby::AsyncOperation { public: Certgen(gnutls_x509_privkey_t key, const Gobby::SlotCertificateGeneratorDone& slot_done): m_key(key), m_slot_done(slot_done), m_cert(NULL), m_error(NULL) { } ~Certgen() { if(m_cert != NULL) gnutls_x509_crt_deinit(m_cert); if(m_error != NULL) g_error_free(m_error); } protected: virtual void run() { InfCertUtilDescription desc; desc.validity = 365 * 24 * 3600; desc.dn_common_name = g_get_user_name(); desc.san_dnsname = g_get_host_name(); m_cert = inf_cert_util_create_self_signed_certificate( m_key, &desc, &m_error); } virtual void finish() { if(m_error != NULL) { m_slot_done(get_handle(), NULL, m_error); } else { gnutls_x509_crt_t cert = m_cert; m_cert = NULL; m_slot_done(get_handle(), cert, NULL); } } private: const gnutls_x509_privkey_t m_key; const Gobby::SlotCertificateGeneratorDone m_slot_done; gnutls_x509_crt_t m_cert; GError* m_error; }; class DHgen: public Gobby::AsyncOperation { public: DHgen(unsigned int bits, const Gobby::SlotDHParamsGeneratorDone& slot_done): m_bits(bits), m_slot_done(slot_done), m_dh_params(NULL), m_error(NULL) { } ~DHgen() { if(m_dh_params != NULL) gnutls_dh_params_deinit(m_dh_params); if(m_error != NULL) g_error_free(m_error); } protected: virtual void run() { // TODO: Actually use the bits number here m_dh_params = inf_cert_util_create_dh_params(&m_error); } virtual void finish() { if(m_error != NULL) { m_slot_done(get_handle(), NULL, m_error); } else { gnutls_dh_params_t dh_params = m_dh_params; m_dh_params = NULL; m_slot_done(get_handle(), dh_params, NULL); } } private: const unsigned int m_bits; const Gobby::SlotDHParamsGeneratorDone m_slot_done; gnutls_dh_params_t m_dh_params; GError* m_error; }; } std::unique_ptr Gobby::create_key(gnutls_pk_algorithm_t algo, unsigned int bits, const SlotKeyGeneratorDone& done_slot) { std::unique_ptr operation( new Keygen(algo, bits, done_slot)); return AsyncOperation::start(std::move(operation)); } std::unique_ptr Gobby::create_self_signed_certificate( gnutls_x509_privkey_t key, const SlotCertificateGeneratorDone& done_slot) { // TODO: Does this really need to be asynchronous? I don't think here // are any blocking calls involved... // TODO: We should make a copy of the key, so that the caller can // delete theirs. std::unique_ptr operation(new Certgen(key, done_slot)); return AsyncOperation::start(std::move(operation)); } std::unique_ptr Gobby::create_dh_params(unsigned int bits, const SlotDHParamsGeneratorDone& done_slot) { std::unique_ptr operation(new DHgen(bits, done_slot)); return AsyncOperation::start(std::move(operation)); } code/core/credentialsgenerator.hpp000066400000000000000000000040301304552132100176260ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_CREDENTIALS_GENERATOR_HPP_ #define _GOBBY_CREDENTIALS_GENERATOR_HPP_ #include "util/asyncoperation.hpp" #include namespace Gobby { typedef AsyncOperation::Handle KeyGeneratorHandle; typedef sigc::slot< void, const KeyGeneratorHandle*, gnutls_x509_privkey_t, const GError* > SlotKeyGeneratorDone; typedef AsyncOperation::Handle CertificateGeneratorHandle; typedef sigc::slot< void, const CertificateGeneratorHandle*, gnutls_x509_crt_t, const GError* > SlotCertificateGeneratorDone; typedef AsyncOperation::Handle DHParamsGeneratorHandle; typedef sigc::slot< void, const DHParamsGeneratorHandle*, gnutls_dh_params_t, const GError* > SlotDHParamsGeneratorDone; std::unique_ptr create_key(gnutls_pk_algorithm_t algo, unsigned int bits, const SlotKeyGeneratorDone& done_slot); std::unique_ptr create_self_signed_certificate(gnutls_x509_privkey_t key, const SlotCertificateGeneratorDone& done_slot); std::unique_ptr create_dh_params(unsigned int bits, const SlotDHParamsGeneratorDone& done_slot); } #endif // _GOBBY_CREDENTIALS_GENERATOR_HPP_ code/core/documentinfostorage.cpp000066400000000000000000000213651304552132100175060ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "core/documentinfostorage.hpp" #include "util/file.hpp" #include "features.hpp" #include #include #include #include namespace { Gobby::DocumentInfoStorage::EolStyle eol_style_from_text(const std::string& text) { if(text == "cr") return Gobby::DocumentInfoStorage::EOL_CR; if(text == "lf") return Gobby::DocumentInfoStorage::EOL_LF; if(text == "crlf") return Gobby::DocumentInfoStorage::EOL_CRLF; // Fallback: return Gobby::DocumentInfoStorage::EOL_LF; } std::string eol_style_to_text(Gobby::DocumentInfoStorage::EolStyle style) { switch(style) { case Gobby::DocumentInfoStorage::EOL_CR: return "cr"; case Gobby::DocumentInfoStorage::EOL_LF: return "lf"; case Gobby::DocumentInfoStorage::EOL_CRLF: return "crlf"; default: return "lf"; } } std::string load_document(xmlpp::Element* node, Gobby::DocumentInfoStorage::Info& info) { std::string root; xmlpp::Node::NodeList list = node->get_children(); for(xmlpp::Node::NodeList::iterator iter = list.begin(); iter != list.end(); ++ iter) { xmlpp::Element* child = dynamic_cast(*iter); if(child == NULL) continue; xmlpp::TextNode* text = child->get_child_text(); if(text == NULL) continue; if(child->get_name() == "root") root = text->get_content(); else if(child->get_name() == "uri") info.uri = text->get_content(); else if(child->get_name() == "eol-style") info.eol_style = eol_style_from_text( text->get_content()); else if(child->get_name() == "encoding") info.encoding = text->get_content(); } return root; } // Location to store the documents file: std::string filename() { return Gobby::config_filename("documents.xml"); } } class Gobby::DocumentInfoStorage::BrowserConn { public: BrowserConn(DocumentInfoStorage& storage, InfBrowser* browser): m_browser(browser) { m_begin_explore_handler = g_signal_connect(G_OBJECT(browser), "begin-request::explore-node", G_CALLBACK(on_begin_request_explore_node_static), &storage); m_node_removed_handler = g_signal_connect(G_OBJECT(browser), "node-removed", G_CALLBACK(on_node_removed_static), &storage); } ~BrowserConn() { g_signal_handler_disconnect(m_browser, m_begin_explore_handler); g_signal_handler_disconnect(m_browser, m_node_removed_handler); } private: InfBrowser* m_browser; gulong m_begin_explore_handler; gulong m_node_removed_handler; }; Gobby::DocumentInfoStorage::DocumentInfoStorage(InfGtkBrowserModel* model): m_model(model) { xmlpp::DomParser parser; try { parser.parse_file(filename()); xmlpp::Document* document = parser.get_document(); if(document) { xmlpp::Element* root = document->get_root_node(); if(root) { init(root); } } } catch(xmlpp::exception& e) { // Could not read file, ignore } g_object_ref(m_model); m_set_browser_handler = g_signal_connect( G_OBJECT(m_model), "set-browser", G_CALLBACK(&on_set_browser_static), this); } Gobby::DocumentInfoStorage::~DocumentInfoStorage() { try { create_directory_with_parents( Glib::path_get_dirname(filename()), 0700); xmlpp::Document document; xmlpp::Element* root = document.create_root_node("documents"); for(InfoMap::iterator iter = m_infos.begin(); iter != m_infos.end(); ++ iter) { xmlpp::Element* child = root->add_child("document"); xmlpp::Element* root_child = child->add_child("root"); root_child->set_child_text(iter->first); xmlpp::Element* uri_child = child->add_child("uri"); uri_child->set_child_text(iter->second.uri); xmlpp::Element* eol_style_child = child->add_child("eol-style"); eol_style_child->set_child_text( eol_style_to_text(iter->second.eol_style)); xmlpp::Element* encoding_child = child->add_child("encoding"); encoding_child->set_child_text(iter->second.encoding); } document.write_to_file_formatted(filename()); } catch(Glib::Exception& e) { g_warning("Could not write documents file: %s", e.what().c_str()); } catch(std::exception& e) { g_warning("Could not write documents file: %s", e.what()); } g_signal_handler_disconnect(m_model, m_set_browser_handler); g_object_unref(m_model); for(BrowserMap::iterator iter = m_browsers.begin(); iter != m_browsers.end(); ++ iter) { delete iter->second; } } void Gobby::DocumentInfoStorage::init(xmlpp::Element* node) { xmlpp::Node::NodeList list = node->get_children(); for(xmlpp::Node::NodeList::iterator iter = list.begin(); iter != list.end(); ++ iter) { xmlpp::Element* child = dynamic_cast(*iter); if(child == NULL) continue; if(child->get_name() == "document") { Info info; std::string root = load_document(child, info); m_infos[root] = info; } } } std::string Gobby::DocumentInfoStorage::get_key(InfBrowser* browser, const InfBrowserIter* iter) const { std::string prefix; if(INFC_IS_BROWSER(browser)) { InfXmlConnection* connection = infc_browser_get_connection(INFC_BROWSER(browser)); g_assert(connection != NULL); gchar* remote_id; g_object_get( G_OBJECT(connection), "remote-id", &remote_id, NULL); prefix = remote_id; g_free(remote_id); } else { prefix = "selfhosted"; } gchar* path = inf_browser_get_path(browser, iter); std::string result = prefix + "?" + path; g_free(path); return result; } const Gobby::DocumentInfoStorage::Info* Gobby::DocumentInfoStorage::get_info(InfBrowser* browser, const InfBrowserIter* iter) const { return get_info(get_key(browser, iter)); } const Gobby::DocumentInfoStorage::Info* Gobby::DocumentInfoStorage::get_info(const std::string& key) const { InfoMap::const_iterator map_iter = m_infos.find(key); if(map_iter != m_infos.end()) return &map_iter->second; return NULL; } void Gobby::DocumentInfoStorage::set_info(InfBrowser* browser, const InfBrowserIter* iter, const Info& info) { set_info(get_key(browser, iter), info); } void Gobby::DocumentInfoStorage::set_info(const std::string& key, const Info& info) { m_infos[key] = info; } void Gobby::DocumentInfoStorage::on_set_browser(GtkTreeIter* iter, InfBrowser* old_browser, InfBrowser* new_browser) { if(old_browser != NULL) { BrowserMap::iterator iter = m_browsers.find(old_browser); g_assert(iter != m_browsers.end()); delete iter->second; m_browsers.erase(iter); } if(new_browser != NULL) { g_assert(m_browsers.find(new_browser) == m_browsers.end()); m_browsers[new_browser] = new BrowserConn(*this, new_browser); // TODO: Remove all infos that refer to no longer existing // documents in all explored directories in browser. Also // add all existing explore requests, to check // subdirectories when the explore finishes (see // on_begin_request_explore_node). } } void Gobby::DocumentInfoStorage:: on_begin_request_explore_node(InfBrowser* browser, InfBrowserIter* iter, InfRequest* request) { // TODO: When request has finished, remove all infos that refer to // no longer existing documents in the explored directory. } void Gobby::DocumentInfoStorage::on_node_removed(InfBrowser* browser, InfBrowserIter* iter, InfRequest* request) { // Remove info when the corresponding document is removed. std::string key = get_key(browser, iter); InfoMap::iterator map_iter = m_infos.find(key); if(map_iter != m_infos.end()) m_infos.erase(map_iter); } code/core/documentinfostorage.hpp000066400000000000000000000072201304552132100175050ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_OPERATIONS_DOCUMENTINFO_STORAGE_HPP_ #define _GOBBY_OPERATIONS_DOCUMENTINFO_STORAGE_HPP_ #include #include #include #include #include namespace Gobby { class DocumentInfoStorage: public sigc::trackable { public: enum EolStyle { EOL_CRLF, EOL_LF, EOL_CR }; struct Info { Glib::ustring uri; EolStyle eol_style; std::string encoding; }; DocumentInfoStorage(InfGtkBrowserModel* model); ~DocumentInfoStorage(); std::string get_key(InfBrowser* browser, const InfBrowserIter* iter) const; const Info* get_info(InfBrowser* browser, const InfBrowserIter* iter) const; const Info* get_info(const std::string& key) const; void set_info(InfBrowser* browser, const InfBrowserIter* iter, const Info& info); void set_info(const std::string& key, const Info& info); protected: static void on_set_browser_static(InfGtkBrowserModel* model, GtkTreePath* path, GtkTreeIter* iter, InfBrowser* old_browser, InfBrowser* new_browser, gpointer user_data) { static_cast(user_data)-> on_set_browser(iter, old_browser, new_browser); } static void on_begin_request_explore_node_static(InfBrowser* browser, InfBrowserIter* iter, InfRequest* request, gpointer user_data) { static_cast(user_data)-> on_begin_request_explore_node(browser, iter, request); } static void on_node_removed_static(InfBrowser* browser, InfBrowserIter* iter, InfRequest* request, gpointer user_data) { static_cast(user_data)-> on_node_removed(browser, iter, request); } void on_set_browser(GtkTreeIter* iter, InfBrowser* old_browser, InfBrowser* new_browser); void on_begin_request_explore_node(InfBrowser* browser, InfBrowserIter* iter, InfRequest* request); void on_node_removed(InfBrowser* browser, InfBrowserIter* iter, InfRequest* request); typedef std::map InfoMap; InfoMap m_infos; class BrowserConn; typedef std::map BrowserMap; BrowserMap m_browsers; gulong m_set_browser_handler; InfGtkBrowserModel* m_model; private: void init(xmlpp::Element* node); }; } #endif // _GOBBY_OPERATIONS_DOCUMENTINFO_STORAGE_HPP_ code/core/filechooser.cpp000066400000000000000000000042631304552132100157270ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "core/filechooser.hpp" #include "util/i18n.hpp" #include #include Gobby::FileChooser::Dialog::Dialog(Gobby::FileChooser& chooser, Gtk::Window& parent, const Glib::ustring& title, Gtk::FileChooserAction action): Gtk::FileChooserDialog(parent, title, action), m_chooser(chooser) { // Set defaults depending on action switch(action) { case Gtk::FILE_CHOOSER_ACTION_SAVE: add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL); add_button(_("_Save"), Gtk::RESPONSE_ACCEPT); set_do_overwrite_confirmation(true); break; case Gtk::FILE_CHOOSER_ACTION_OPEN: add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL); add_button(_("_Open"), Gtk::RESPONSE_ACCEPT); break; default: g_assert_not_reached(); break; } set_local_only(false); set_current_folder_uri(m_chooser.get_current_folder_uri()); } Gobby::FileChooser::Dialog::~Dialog() { m_chooser.set_current_folder_uri(get_current_folder_uri()); } Gobby::FileChooser::FileChooser(): m_current_folder_uri(Glib::filename_to_uri(Glib::get_current_dir())) { } const std::string& Gobby::FileChooser::get_current_folder_uri() const { return m_current_folder_uri; } void Gobby::FileChooser::set_current_folder_uri(const std::string& uri) { m_current_folder_uri = uri; } code/core/filechooser.hpp000066400000000000000000000030441304552132100157300ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_FILE_CHOOSER_HPP_ #define _GOBBY_FILE_CHOOSER_HPP_ #include #include namespace Gobby { // This class manages a common "current folder uri" for file chooser // dialogs. class FileChooser { public: class Dialog: public Gtk::FileChooserDialog { public: Dialog(Gobby::FileChooser& chooser, Gtk::Window& parent, const Glib::ustring& title, Gtk::FileChooserAction action); ~Dialog(); protected: Gobby::FileChooser& m_chooser; }; FileChooser(); const std::string& get_current_folder_uri() const; void set_current_folder_uri(const std::string& uri); private: std::string m_current_folder_uri; }; } #endif // _GOBBY_FILE_CHOOSER_HPP_ code/core/folder.cpp000066400000000000000000000207201304552132100146740ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "core/folder.hpp" #include "core/textsessionuserview.hpp" #include "core/sessionuserview.hpp" #include "core/chattablabel.hpp" #include "core/texttablabel.hpp" #include "util/file.hpp" #include #include #include #include // For std::cerr #include namespace { class KeyMap { public: static const unsigned int nval = ~0u; KeyMap() { m_keyvals[GDK_KEY_0] = 9; m_keyvals[GDK_KEY_1] = 0; m_keyvals[GDK_KEY_2] = 1; m_keyvals[GDK_KEY_3] = 2; m_keyvals[GDK_KEY_4] = 3; m_keyvals[GDK_KEY_5] = 4; m_keyvals[GDK_KEY_6] = 5; m_keyvals[GDK_KEY_7] = 6; m_keyvals[GDK_KEY_8] = 7; m_keyvals[GDK_KEY_9] = 8; } unsigned int lookup(guint key) const { map_type::const_iterator iter = m_keyvals.find(key); if(iter == m_keyvals.end() ) return nval; return iter->second; } private: typedef std::map map_type; map_type m_keyvals; }; void record(InfTextSession* session, const Glib::ustring& title) { std::string dirname = Glib::build_filename( Glib::get_home_dir(), ".infinote-records"); std::string filename = Glib::build_filename( dirname, title + ".record.xml"); try { Gobby::create_directory_with_parents(dirname, 0700); InfAdoptedSessionRecord* record = inf_adopted_session_record_new( INF_ADOPTED_SESSION(session)); GError* error = NULL; inf_adopted_session_record_start_recording( record, filename.c_str(), &error); if(error != NULL) { g_object_unref(record); std::string what = error->message; g_error_free(error); throw std::runtime_error(what); } g_object_set_data_full( G_OBJECT(session), "GOBBY_SESSION_RECORD", record, g_object_unref); } catch(std::exception& ex) { std::cerr << "Failed to create record '" << filename << "': " << ex.what() << std::endl; } } } Gobby::Folder::Folder(bool hide_single_tab, Preferences& preferences, GtkSourceLanguageManager* lang_manager): m_hide_single_tab(hide_single_tab), m_preferences(preferences), m_lang_manager(lang_manager), m_document_userlist_width(Gio::Settings::create( "de.0x539.gobby.state.window"), "document-userlist-width"), m_chat_userlist_width(Gio::Settings::create( "de.0x539.gobby.state.window"), "chat-userlist-width") { set_scrollable(true); set_show_border(false); if(hide_single_tab) set_show_tabs(false); } Gobby::Folder::~Folder() { // Remove all documents explicitely, so that all sessions are closed, // and records finished. while(get_n_pages()) remove_document(get_document(0)); } // TODO: Share common code of add_text_session and add_chat_session Gobby::TextSessionView& Gobby::Folder::add_text_session(InfTextSession* session, const Glib::ustring& title, const Glib::ustring& path, const Glib::ustring& hostname, const std::string& info_storage_key) { TextSessionView* view = Gtk::manage( new TextSessionView(session, title, path, hostname, info_storage_key, m_preferences, m_lang_manager)); view->show(); m_signal_document_added.emit(*view); TextSessionUserView* userview = Gtk::manage( new TextSessionUserView( *view, true, m_preferences.appearance.show_document_userlist, m_document_userlist_width)); userview->show(); TabLabel* tablabel = Gtk::manage(new TextTabLabel(*this, *view)); tablabel->signal_close_request().connect( sigc::bind( sigc::mem_fun(*this, &Folder::on_tab_close_request), sigc::ref(*view))); tablabel->show(); append_page(*userview, *tablabel); set_tab_reorderable(*userview, true); // Record the session, for debugging purposes: record(session, title); if(m_hide_single_tab && get_n_pages() > 1) set_show_tabs(true); return *view; } Gobby::ChatSessionView& Gobby::Folder::add_chat_session(InfChatSession* session, const Glib::ustring& title, const Glib::ustring& path, const Glib::ustring& hostname) { ChatSessionView* view = Gtk::manage( new ChatSessionView(session, title, path, hostname, m_preferences)); view->show(); m_signal_document_added.emit(*view); SessionUserView* userview = Gtk::manage( new SessionUserView( *view, false, m_preferences.appearance.show_chat_userlist, m_chat_userlist_width)); userview->show(); TabLabel* tablabel = Gtk::manage( new ChatTabLabel(*this, *view, path != "")); tablabel->signal_close_request().connect( sigc::bind( sigc::mem_fun(*this, &Folder::on_tab_close_request), sigc::ref(*view))); tablabel->show(); append_page(*userview, *tablabel); set_tab_reorderable(*userview, true); if(m_hide_single_tab && get_n_pages() > 1) set_show_tabs(true); return *view; } void Gobby::Folder::remove_document(SessionView& view) { m_signal_document_removed.emit(view); // Finish the record InfSession* session = view.get_session(); g_object_set_data(G_OBJECT(session), "GOBBY_SESSION_RECORD", NULL); g_object_ref(session); // This relies on the sessionuserview being the direct parent of // view - maybe we should make a loop here instead which searches // the folder in the widget hierarchy, to be more robust. remove_page(*view.get_parent()); g_object_unref(session); if(get_n_pages() == 0) m_signal_document_changed.emit(NULL); if(m_hide_single_tab && get_n_pages() <= 1) set_show_tabs(false); } Gobby::SessionView& Gobby::Folder::get_document(unsigned int n) const { const SessionUserView* child = static_cast(get_nth_page(n)); if(!child) throw std::logic_error( "Gobby::Folder::get_document: out of bounds"); return child->get_session_view(); } Gobby::SessionView* Gobby::Folder::lookup_document(InfSession* session) const { const unsigned int n_pages = get_n_pages(); for(unsigned int i = 0; i < n_pages; ++i) if(get_document(i).get_session() == session) return &get_document(i); return NULL; } Gobby::SessionView* Gobby::Folder::get_current_document() const { const SessionUserView* child = static_cast( get_nth_page(get_current_page())); if(!child) return NULL; return &child->get_session_view(); } void Gobby::Folder::switch_to_document(SessionView& document) { // Again, here we rely on document being the direct child of // the SessionUserView... set_current_page(page_num(*document.get_parent())); } void Gobby::Folder::on_tab_close_request(SessionView& view) { if(m_signal_document_close_request.emit(view)) remove_document(view); } void Gobby::Folder::on_switch_page(Gtk::Widget* page, guint page_num) { Notebook::on_switch_page(page, page_num); // SessionUserView& view = // *static_cast(get_nth_page(page_num)); SessionUserView& view = *static_cast(page); m_signal_document_changed.emit(&view.get_session_view()); } bool Gobby::Folder::on_key_press_event(GdkEventKey* event) { static KeyMap keymap; if( (event->state & GDK_MOD1_MASK) == GDK_MOD1_MASK) { unsigned int page = keymap.lookup(event->keyval); if(page != KeyMap::nval) { set_current_page(page); return true; } } if( (event->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)) == (GDK_CONTROL_MASK | GDK_MOD1_MASK)) { int offset = 0; if(event->keyval == GDK_KEY_Page_Up) offset = -1; if(event->keyval == GDK_KEY_Page_Down) offset = 1; if(offset != 0) { int res = get_current_page() + offset + get_n_pages(); set_current_page(res % get_n_pages() ); return true; } } return false; } code/core/folder.hpp000066400000000000000000000071171304552132100147060ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_FOLDER_HPP_ #define _GOBBY_FOLDER_HPP_ #include "core/textsessionview.hpp" #include "core/chatsessionview.hpp" #include "core/preferences.hpp" #include "util/defaultaccumulator.hpp" #include #include #include namespace Gobby { class Folder : public Gtk::Notebook { public: // TODO chat: This should be SignalSessionAdded/Removed/Changed typedef sigc::signal SignalDocumentAdded; typedef sigc::signal SignalDocumentRemoved; typedef sigc::signal SignalDocumentChanged; typedef sigc::signal:: accumulated > SignalDocumentCloseRequest; // TODO chat: Should not require langmgr Folder(bool hide_single_tab, Preferences& preferences, GtkSourceLanguageManager* lang_manager); ~Folder(); TextSessionView& add_text_session(InfTextSession* session, const Glib::ustring& title, const Glib::ustring& path, const Glib::ustring& hostname, const std::string& info_storage_key); ChatSessionView& add_chat_session(InfChatSession* session, const Glib::ustring& title, const Glib::ustring& path, const Glib::ustring& hostname); void remove_document(SessionView& view); SessionView& get_document(unsigned int n) const; SessionView* lookup_document(InfSession* session) const; SessionView* get_current_document() const; void switch_to_document(SessionView& document); SignalDocumentAdded signal_document_added() const { return m_signal_document_added; } SignalDocumentRemoved signal_document_removed() const { return m_signal_document_removed; } SignalDocumentChanged signal_document_changed() const { return m_signal_document_changed; } SignalDocumentCloseRequest signal_document_close_request() const { return m_signal_document_close_request; } protected: virtual void on_switch_page(Gtk::Widget* page, guint page_num); virtual bool on_key_press_event(GdkEventKey* event); void on_tab_close_request(SessionView& window); const bool m_hide_single_tab; Preferences& m_preferences; GtkSourceLanguageManager* m_lang_manager; // Persistent state Preferences::Option m_document_userlist_width; Preferences::Option m_chat_userlist_width; SignalDocumentAdded m_signal_document_added; SignalDocumentRemoved m_signal_document_removed; SignalDocumentChanged m_signal_document_changed; SignalDocumentCloseRequest m_signal_document_close_request; }; } #endif // _GOBBY_FOLDER_HPP_ code/core/foldermanager.cpp000066400000000000000000000271161304552132100162350ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "core/foldermanager.hpp" #include "util/i18n.hpp" #include #include class Gobby::FolderManager::BrowserInfo { public: BrowserInfo(FolderManager& manager, InfBrowser* browser): m_browser(browser) { g_object_ref(m_browser); m_unsubscribe_session_handler = g_signal_connect( G_OBJECT(browser), "unsubscribe-session", G_CALLBACK(&on_unsubscribe_session_static), &manager); } ~BrowserInfo() { g_signal_handler_disconnect(G_OBJECT(m_browser), m_unsubscribe_session_handler); g_object_unref(m_browser); } private: InfBrowser* m_browser; gulong m_unsubscribe_session_handler; }; class Gobby::FolderManager::SessionInfo { public: SessionInfo(Folder& folder, InfBrowser* browser, const InfBrowserIter* iter, InfSessionProxy* proxy): m_folder(folder), m_browser(browser), m_proxy(proxy) { if(browser != NULL && iter != NULL) m_browser_iter = *iter; else m_browser = NULL; if(proxy != NULL) g_object_ref(proxy); } ~SessionInfo() { if(m_proxy != NULL) g_object_unref(m_proxy); } void reset_browser() { m_browser = NULL; } InfBrowser* get_browser() { return m_browser; } const InfBrowserIter* get_browser_iter() { return m_browser == NULL ? NULL : &m_browser_iter; } Folder& get_folder() { return m_folder; } InfSessionProxy* get_proxy() { return m_proxy; } private: Folder& m_folder; InfBrowser* m_browser; InfBrowserIter m_browser_iter; InfSessionProxy* m_proxy; }; Gobby::FolderManager::FolderManager(Browser& browser, DocumentInfoStorage& info_storage, Folder& text_folder, Folder& chat_folder): m_browser(browser), m_info_storage(info_storage), m_text_folder(text_folder), //false, preferences, m_lang_manager), m_chat_folder(chat_folder) //true, preferences, m_lang_manager) { InfGtkBrowserModel* model = INF_GTK_BROWSER_MODEL(browser.get_store()); m_set_browser_handler = g_signal_connect( G_OBJECT(model), "set-browser", G_CALLBACK(&on_set_browser_static), this); // Add already existing browsers GtkTreeIter iter; GtkTreeModel* treemodel = GTK_TREE_MODEL(model); for(gboolean have_entry = gtk_tree_model_get_iter_first(treemodel, &iter); have_entry == TRUE; have_entry = gtk_tree_model_iter_next(treemodel, &iter)) { InfBrowser* browser; gtk_tree_model_get( treemodel, &iter, INF_GTK_BROWSER_MODEL_COL_BROWSER, &browser, -1); if(browser != NULL) { m_browser_map[browser] = new BrowserInfo(*this, browser); g_object_unref(browser); } } m_text_document_added_connection = m_text_folder.signal_document_added().connect( sigc::mem_fun( *this, &FolderManager::on_text_document_added)); m_chat_document_added_connection = m_chat_folder.signal_document_added().connect( sigc::mem_fun( *this, &FolderManager::on_chat_document_added)); m_text_folder.signal_document_removed().connect( sigc::mem_fun( *this, &FolderManager::on_document_removed)); m_chat_folder.signal_document_removed().connect( sigc::mem_fun( *this, &FolderManager::on_document_removed)); } Gobby::FolderManager::~FolderManager() { for(BrowserMap::iterator iter = m_browser_map.begin(); iter != m_browser_map.end(); ++ iter) { delete iter->second; } for(SessionMap::iterator iter = m_session_map.begin(); iter != m_session_map.end(); ++iter) { delete iter->second; } g_signal_handler_disconnect( INF_GTK_BROWSER_MODEL(m_browser.get_store()), m_set_browser_handler); } void Gobby::FolderManager::add_document(InfBrowser* browser, const InfBrowserIter* iter, InfSessionProxy* proxy, UserJoinRef userjoin) { gchar* hostname; if(INFC_IS_BROWSER(browser)) { InfXmlConnection* connection = infc_browser_get_connection(INFC_BROWSER(browser)); g_object_get( G_OBJECT(connection), "remote-hostname", &hostname, NULL); } else if(INFD_IS_DIRECTORY(browser)) { hostname = g_strdup(g_get_host_name()); } else { g_assert_not_reached(); hostname = NULL; } InfSession* session; g_object_get(G_OBJECT(proxy), "session", &session, NULL); TextSessionView* text_view = NULL; Folder* folder; SessionView* view; if(INF_TEXT_IS_SESSION(session)) { gchar* path = inf_browser_get_path(browser, iter); m_text_document_added_connection.block(); text_view = &m_text_folder.add_text_session( INF_TEXT_SESSION(session), inf_browser_get_node_name(browser, iter), path, hostname, m_info_storage.get_key(browser, iter)); m_text_document_added_connection.unblock(); folder = &m_text_folder; view = text_view; g_free(path); } else if(INF_IS_CHAT_SESSION(session)) { if(iter == NULL) { m_chat_document_added_connection.block(); view = &m_chat_folder.add_chat_session( INF_CHAT_SESSION(session), hostname, "", hostname); m_chat_document_added_connection.unblock(); } else { gchar* path = inf_browser_get_path(browser, iter); m_chat_document_added_connection.block(); view = &m_chat_folder.add_chat_session( INF_CHAT_SESSION(session), inf_browser_get_node_name(browser, iter), path, hostname); m_chat_document_added_connection.unblock(); g_free(path); } folder = &m_chat_folder; } else { // Cannot happen, because we don't have any other note plugins // installed in the browser. g_assert_not_reached(); } g_free(hostname); // Highlight the newly created session folder->switch_to_document(*view); if(text_view) gtk_widget_grab_focus(GTK_WIDGET(text_view->get_text_view())); if(iter) m_browser.set_selected(browser, iter); g_assert(m_session_map.find(session) == m_session_map.end()); m_session_map[session] = new SessionInfo(*folder, browser, iter, proxy); g_object_unref(session); m_signal_document_added.emit( browser, iter, proxy, *folder, *view, userjoin); } void Gobby::FolderManager::remove_document(SessionView& view) { SessionMap::const_iterator iter = m_session_map.find(view.get_session()); g_assert(iter != m_session_map.end()); iter->second->get_folder().remove_document(view); } Gobby::SessionView* Gobby::FolderManager::lookup_document(InfSession* session) const { SessionMap::const_iterator iter = m_session_map.find(session); if(iter == m_session_map.end()) return NULL; return iter->second->get_folder().lookup_document(session); } void Gobby::FolderManager::switch_to_document(SessionView& view) { SessionMap::iterator iter = m_session_map.find(view.get_session()); g_assert(iter != m_session_map.end()); iter->second->get_folder().switch_to_document(view); } void Gobby::FolderManager::on_set_browser(InfGtkBrowserModel* model, GtkTreeIter* iter, InfBrowser* old_browser, InfBrowser* new_browser) { if(old_browser != NULL) { BrowserMap::iterator iter = m_browser_map.find(old_browser); g_assert(iter != m_browser_map.end()); delete iter->second; m_browser_map.erase(iter); } if(new_browser != NULL) { g_assert(m_browser_map.find(new_browser) == m_browser_map.end()); m_browser_map[new_browser] = new BrowserInfo(*this, new_browser); } } void Gobby::FolderManager::on_unsubscribe_session(InfBrowser* browser, const InfBrowserIter* iter, InfSessionProxy* proxy, InfRequest* request) { InfSession* session; g_object_get(G_OBJECT(proxy), "session", &session, NULL); SessionMap::iterator session_iter = m_session_map.find(session); // Note that the session might not be in the session map, for example // if we are subscribed but do not show a document for the session. if(session_iter != m_session_map.end()) { // Browser entry disappeared for the session, so invalidate // our iterator. The session is now "floating", without any // browser entry. session_iter->second->reset_browser(); // When the subscription group of the session is still // available, then close the session. This can essentially // only happen on the server side, and it closes the session // when it is removed from the document tree. In principle // the session could go on until the document is closed by // the server, but this probably causes more confusion than // anything else. So we close it. // TODO: It would be nice to keep the session itself alive, // and to only unsubscribe all clients and set the local users // to unavailable. if(inf_session_get_subscription_group(session) != NULL) { lookup_document(session)->set_info( _("The document has been removed from the server."), true); inf_session_close(session); } } g_object_unref(session); } void Gobby::FolderManager::on_text_document_added(SessionView& view) { // This should not happen, because we only hand out const folders. g_assert_not_reached(); InfSession* session = view.get_session(); // Do as if the document was added "normally", but we cannot associate // either a browser entry or a session proxy. g_assert(m_session_map.find(session) == m_session_map.end()); m_session_map[session] = new SessionInfo(m_text_folder, NULL, NULL, NULL); m_signal_document_added.emit(NULL, NULL, NULL, m_text_folder, view, NULL); } void Gobby::FolderManager::on_chat_document_added(SessionView& view) { // This should not happen, because we only hand out const folders. g_assert_not_reached(); InfSession* session = view.get_session(); // Do as if the document was added "normally", but we cannot associate // either a browser entry or a session proxy. g_assert(m_session_map.find(session) == m_session_map.end()); m_session_map[session] = new SessionInfo(m_chat_folder, NULL, NULL, NULL); m_signal_document_added.emit(NULL, NULL, NULL, m_chat_folder, view, NULL); } void Gobby::FolderManager::on_document_removed(SessionView& view) { InfSession* session = view.get_session(); SessionMap::iterator session_iter = m_session_map.find(session); g_assert(session_iter != m_session_map.end()); Folder& folder = session_iter->second->get_folder(); InfBrowser* browser = session_iter->second->get_browser(); InfSessionProxy* proxy = session_iter->second->get_proxy(); const InfBrowserIter* iter = session_iter->second->get_browser_iter(); InfBrowserIter local_iter; if(iter != NULL) local_iter = *iter; g_object_ref(proxy); delete session_iter->second; m_session_map.erase(session_iter); m_signal_document_removed.emit( browser, browser != NULL ? &local_iter : NULL, proxy, folder, view); g_object_unref(proxy); } code/core/foldermanager.hpp000066400000000000000000000111231304552132100162310ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_FOLDERMANAGER_HPP_ #define _GOBBY_FOLDERMANAGER_HPP_ #include "core/browser.hpp" #include "core/folder.hpp" #include "core/userjoin.hpp" #include "core/documentinfostorage.hpp" #include #include namespace Gobby { // The FolderManager class manages the two folders, for text documents and // chat documents. On top of this, it also manages a relation of browser // entries to sessions and session proxies. class FolderManager: public sigc::trackable { public: typedef std::unique_ptr* UserJoinRef; typedef sigc::signal SignalDocumentAdded; typedef sigc::signal SignalDocumentRemoved; FolderManager(Browser& browser, DocumentInfoStorage& info_storage, Folder& text_folder, Folder& chat_folder); ~FolderManager(); const Folder& get_text_folder() const { return m_text_folder; } const Folder& get_chat_folder() const { return m_chat_folder; } // Add a SessionView for the given session void add_document(InfBrowser* browser, const InfBrowserIter* iter, InfSessionProxy* proxy, UserJoinRef userjoin); void remove_document(SessionView& view); SessionView* lookup_document(InfSession* session) const; void switch_to_document(SessionView& view); // Emitted whenever a document has been added, either for text or // for chat sessions. SignalDocumentAdded signal_document_added() const { return m_signal_document_added; } SignalDocumentRemoved signal_document_removed() const { return m_signal_document_removed; } protected: static void on_set_browser_static(InfGtkBrowserModel* model, GtkTreePath* path, GtkTreeIter* iter, InfBrowser* old_browser, InfBrowser* new_browser, gpointer user_data) { static_cast(user_data)->on_set_browser( model, iter, old_browser, new_browser); } static void on_unsubscribe_session_static(InfBrowser* browser, const InfBrowserIter* iter, InfSessionProxy* proxy, InfRequest* request, gpointer user_data) { static_cast(user_data)-> on_unsubscribe_session(browser, iter, proxy, request); } void on_set_browser(InfGtkBrowserModel* model, GtkTreeIter* iter, InfBrowser* old_browser, InfBrowser* new_browser); void on_unsubscribe_session(InfBrowser* browser, const InfBrowserIter* iter, InfSessionProxy* proxy, InfRequest* request); void on_text_document_added(SessionView& view); void on_chat_document_added(SessionView& view); void on_document_removed(SessionView& view); Browser& m_browser; DocumentInfoStorage& m_info_storage; Folder& m_text_folder; Folder& m_chat_folder; sigc::connection m_text_document_added_connection; sigc::connection m_chat_document_added_connection; gulong m_set_browser_handler; class BrowserInfo; typedef std::map BrowserMap; BrowserMap m_browser_map; class SessionInfo; typedef std::map SessionMap; SessionMap m_session_map; SignalDocumentAdded m_signal_document_added; SignalDocumentRemoved m_signal_document_removed; }; } #endif // _GOBBY_FOLDERMANAGER_HPP_ code/core/gobject/000077500000000000000000000000001304552132100143315ustar00rootroot00000000000000code/core/gobject/Makefile.am000066400000000000000000000001741304552132100163670ustar00rootroot00000000000000gobby_0_5_SOURCES += \ code/core/gobject/gobby-undo-manager.c noinst_HEADERS += \ code/core/gobject/gobby-undo-manager.h code/core/gobject/gobby-undo-manager.c000066400000000000000000000252631304552132100201620ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "core/gobject/gobby-undo-manager.h" #include typedef struct _GobbyUndoManagerPrivate GobbyUndoManagerPrivate; struct _GobbyUndoManagerPrivate { InfTextSession* session; InfTextUndoGrouping* undo_grouping; }; enum { PROP_0, PROP_SESSION, PROP_UNDO_GROUPING }; #define GOBBY_UNDO_MANAGER_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), GOBBY_TYPE_UNDO_MANAGER, GobbyUndoManagerPrivate)) static void gobby_undo_manager_undo_manager_iface_init(GtkSourceUndoManagerIface* iface); G_DEFINE_TYPE_WITH_CODE(GobbyUndoManager, gobby_undo_manager, G_TYPE_OBJECT, G_ADD_PRIVATE(GobbyUndoManager) G_IMPLEMENT_INTERFACE(GTK_SOURCE_TYPE_UNDO_MANAGER, gobby_undo_manager_undo_manager_iface_init)) static void gobby_undo_manager_can_undo_changed_cb(InfAdoptedAlgorithm* algorithm, InfAdoptedUser* user, gboolean can_undo, gpointer user_data) { GobbyUndoManager* undo_manager; GobbyUndoManagerPrivate* priv; undo_manager = GOBBY_UNDO_MANAGER(user_data); priv = GOBBY_UNDO_MANAGER_PRIVATE(undo_manager); InfAdoptedUser* our_user; g_object_get(G_OBJECT(priv->undo_grouping), "user", &our_user, NULL); if(our_user == user) { gtk_source_undo_manager_can_undo_changed( GTK_SOURCE_UNDO_MANAGER(undo_manager) ); } g_object_unref(our_user); } static void gobby_undo_manager_can_redo_changed_cb(InfAdoptedAlgorithm* algorithm, InfAdoptedUser* user, gboolean can_redo, gpointer user_data) { GobbyUndoManager* undo_manager; GobbyUndoManagerPrivate* priv; undo_manager = GOBBY_UNDO_MANAGER(user_data); priv = GOBBY_UNDO_MANAGER_PRIVATE(undo_manager); InfAdoptedUser* our_user; g_object_get(G_OBJECT(priv->undo_grouping), "user", &our_user, NULL); if(our_user == user) { gtk_source_undo_manager_can_redo_changed( GTK_SOURCE_UNDO_MANAGER(undo_manager) ); } g_object_unref(our_user); } static void gobby_undo_manager_set_session(GobbyUndoManager* undo_manager, InfTextSession* session) { GobbyUndoManagerPrivate* priv; InfAdoptedAlgorithm* algorithm; priv = GOBBY_UNDO_MANAGER_PRIVATE(undo_manager); if(priv->session != NULL) { algorithm = inf_adopted_session_get_algorithm( INF_ADOPTED_SESSION(priv->session) ); g_signal_handlers_disconnect_by_func( G_OBJECT(algorithm), G_CALLBACK(gobby_undo_manager_can_undo_changed_cb), undo_manager ); g_signal_handlers_disconnect_by_func( G_OBJECT(algorithm), G_CALLBACK(gobby_undo_manager_can_redo_changed_cb), undo_manager ); g_object_unref(priv->session); } priv->session = session; if(session != NULL) { g_object_ref(session); algorithm = inf_adopted_session_get_algorithm( INF_ADOPTED_SESSION(session) ); g_signal_connect( G_OBJECT(algorithm), "can-undo-changed", G_CALLBACK(gobby_undo_manager_can_undo_changed_cb), undo_manager ); g_signal_connect( G_OBJECT(algorithm), "can-redo-changed", G_CALLBACK(gobby_undo_manager_can_redo_changed_cb), undo_manager ); } } static void gobby_undo_manager_init(GobbyUndoManager* undo_manager) { GobbyUndoManagerPrivate* priv; priv = GOBBY_UNDO_MANAGER_PRIVATE(undo_manager); priv->session = NULL; priv->undo_grouping = NULL; } static void gobby_undo_manager_dispose(GObject* object) { GobbyUndoManager* undo_manager; GobbyUndoManagerPrivate* priv; undo_manager = GOBBY_UNDO_MANAGER(object); priv = GOBBY_UNDO_MANAGER_PRIVATE(undo_manager); gobby_undo_manager_set_session(undo_manager, NULL); if(priv->undo_grouping != NULL) { g_object_unref(priv->undo_grouping); priv->undo_grouping = NULL; } G_OBJECT_CLASS(gobby_undo_manager_parent_class)->dispose(object); } static void gobby_undo_manager_finalize(GObject* object) { GobbyUndoManager* undo_manager; GobbyUndoManagerPrivate* priv; undo_manager = GOBBY_UNDO_MANAGER(object); priv = GOBBY_UNDO_MANAGER_PRIVATE(undo_manager); G_OBJECT_CLASS(gobby_undo_manager_parent_class)->finalize(object); } static void gobby_undo_manager_set_property(GObject* object, guint prop_id, const GValue* value, GParamSpec* pspec) { GobbyUndoManager* undo_manager; GobbyUndoManagerPrivate* priv; undo_manager = GOBBY_UNDO_MANAGER(object); priv = GOBBY_UNDO_MANAGER_PRIVATE(undo_manager); switch(prop_id) { case PROP_SESSION: gobby_undo_manager_set_session( undo_manager, INF_TEXT_SESSION(g_value_get_object(value)) ); break; case PROP_UNDO_GROUPING: if(priv->undo_grouping != NULL) g_object_unref(priv->undo_grouping); priv->undo_grouping = INF_TEXT_UNDO_GROUPING(g_value_dup_object(value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; } } static void gobby_undo_manager_get_property(GObject* object, guint prop_id, GValue* value, GParamSpec* pspec) { GobbyUndoManager* undo_manager; GobbyUndoManagerPrivate* priv; undo_manager = GOBBY_UNDO_MANAGER(object); priv = GOBBY_UNDO_MANAGER_PRIVATE(undo_manager); switch(prop_id) { case PROP_SESSION: g_value_set_object(value, priv->session); break; case PROP_UNDO_GROUPING: g_value_set_object(value, priv->undo_grouping); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; } } static void gobby_undo_manager_class_init(GobbyUndoManagerClass* undo_manager_class) { GObjectClass* object_class; object_class = G_OBJECT_CLASS(undo_manager_class); object_class->dispose = gobby_undo_manager_dispose; object_class->finalize = gobby_undo_manager_finalize; object_class->set_property = gobby_undo_manager_set_property; object_class->get_property = gobby_undo_manager_get_property; g_object_class_install_property( object_class, PROP_SESSION, g_param_spec_object( "session", "Session", "The session on which to operate", INF_TEXT_TYPE_SESSION, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY ) ); g_object_class_install_property( object_class, PROP_UNDO_GROUPING, g_param_spec_object( "undo-grouping", "Undo Grouping", "The undo grouping to use", INF_TEXT_TYPE_UNDO_GROUPING, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY ) ); } static gboolean gobby_undo_manager_can_undo(GtkSourceUndoManager* manager) { GobbyUndoManager* undo_manager; GobbyUndoManagerPrivate* priv; InfAdoptedAlgorithm* algorithm; InfAdoptedUser* user; gboolean can_undo; undo_manager = GOBBY_UNDO_MANAGER(manager); priv = GOBBY_UNDO_MANAGER_PRIVATE(undo_manager); algorithm = inf_adopted_session_get_algorithm(INF_ADOPTED_SESSION(priv->session)); g_object_get(G_OBJECT(priv->undo_grouping), "user", &user, NULL); can_undo = inf_adopted_algorithm_can_undo(algorithm, user); g_object_unref(user); return can_undo; } static gboolean gobby_undo_manager_can_redo(GtkSourceUndoManager* manager) { GobbyUndoManager* undo_manager; GobbyUndoManagerPrivate* priv; InfAdoptedAlgorithm* algorithm; InfAdoptedUser* user; gboolean can_redo; undo_manager = GOBBY_UNDO_MANAGER(manager); priv = GOBBY_UNDO_MANAGER_PRIVATE(undo_manager); algorithm = inf_adopted_session_get_algorithm(INF_ADOPTED_SESSION(priv->session)); g_object_get(G_OBJECT(priv->undo_grouping), "user", &user, NULL); can_redo = inf_adopted_algorithm_can_redo(algorithm, user); g_object_unref(user); return can_redo; } static void gobby_undo_manager_undo(GtkSourceUndoManager* manager) { GobbyUndoManager* undo_manager; GobbyUndoManagerPrivate* priv; InfAdoptedUser* user; guint n_undo; undo_manager = GOBBY_UNDO_MANAGER(manager); priv = GOBBY_UNDO_MANAGER_PRIVATE(undo_manager); g_object_get(G_OBJECT(priv->undo_grouping), "user", &user, NULL); n_undo = inf_adopted_undo_grouping_get_undo_size( INF_ADOPTED_UNDO_GROUPING(priv->undo_grouping) ); g_assert(n_undo > 0); inf_adopted_session_undo(INF_ADOPTED_SESSION(priv->session), user, n_undo); g_object_unref(user); } static void gobby_undo_manager_redo(GtkSourceUndoManager* manager) { GobbyUndoManager* undo_manager; GobbyUndoManagerPrivate* priv; InfAdoptedUser* user; guint n_redo; undo_manager = GOBBY_UNDO_MANAGER(manager); priv = GOBBY_UNDO_MANAGER_PRIVATE(undo_manager); g_object_get(G_OBJECT(priv->undo_grouping), "user", &user, NULL); n_redo = inf_adopted_undo_grouping_get_redo_size( INF_ADOPTED_UNDO_GROUPING(priv->undo_grouping) ); g_assert(n_redo > 0); inf_adopted_session_redo(INF_ADOPTED_SESSION(priv->session), user, n_redo); g_object_unref(user); } static void gobby_undo_manager_begin_not_undoable_action(GtkSourceUndoManager* manager) { /* Unused */ } static void gobby_undo_manager_end_not_undoable_action(GtkSourceUndoManager* manager) { /* Unused */ } static void gobby_undo_manager_undo_manager_iface_init(GtkSourceUndoManagerIface* iface) { iface->can_undo = gobby_undo_manager_can_undo; iface->can_redo = gobby_undo_manager_can_redo; iface->undo = gobby_undo_manager_undo; iface->redo = gobby_undo_manager_redo; iface->begin_not_undoable_action = gobby_undo_manager_begin_not_undoable_action; iface->end_not_undoable_action = gobby_undo_manager_end_not_undoable_action; } GobbyUndoManager* gobby_undo_manager_new(InfTextSession* session, InfTextUndoGrouping* undo_grouping) { GObject* object; object = g_object_new( GOBBY_TYPE_UNDO_MANAGER, "session", session, "undo-grouping", undo_grouping, NULL ); return GOBBY_UNDO_MANAGER(object); } code/core/gobject/gobby-undo-manager.h000066400000000000000000000046541304552132100201700ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef __GOBBY_UNDO_MANAGER_H__ #define __GOBBY_UNDO_MANAGER_H__ #include #include #include G_BEGIN_DECLS #define GOBBY_TYPE_UNDO_MANAGER (gobby_undo_manager_get_type()) #define GOBBY_UNDO_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GOBBY_TYPE_UNDO_MANAGER, GobbyUndoManager)) #define GOBBY_UNDO_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GOBBY_TYPE_UNDO_MANAGER, GobbyUndoManagerClass)) #define GOBBY_IS_UNDO_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GOBBY_TYPE_UNDO_MANAGER)) #define GOBBY_IS_UNDO_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GOBBY_TYPE_UNDO_MANAGER)) #define GOBBY_UNDO_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GOBBY_TYPE_UNDO_MANAGER, GobbyUndoManagerClass)) typedef struct _GobbyUndoManager GobbyUndoManager; typedef struct _GobbyUndoManagerClass GobbyUndoManagerClass; /** * GobbyUndoManagerClass: * * This structure does not contain any public fields. */ struct _GobbyUndoManagerClass { /*< private >*/ GObjectClass parent_class; }; /** * GobbyUndoManager: * * #GobbyUndoManager is an opaque data type. You should only access it via the * public API functions. */ struct _GobbyUndoManager { /*< private >*/ GObject parent; }; GType gobby_undo_manager_get_type(void) G_GNUC_CONST; GobbyUndoManager* gobby_undo_manager_new(InfTextSession* session, InfTextUndoGrouping* grouping); G_END_DECLS #endif /* __GOBBY_UNDO_MANAGER_H__ */ /* vim:set et sw=2 ts=2: */ code/core/huebutton.cpp000066400000000000000000000067101304552132100154410ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "core/huebutton.hpp" #include "util/i18n.hpp" #include Gobby::HueButton::HueButton(GtkColorButton* cobject, const Glib::RefPtr& builder): Gtk::ColorButton(cobject), m_saturation(1.0), m_value(1.0) { set_hue(1.0); } Gobby::HueButton::HueButton(const Glib::ustring& title): m_saturation(1.0), m_value(1.0) { set_title(title); set_hue(1.0); } double Gobby::HueButton::get_hue() const { const Gdk::RGBA rgba = get_rgba(); double h, s, v; gtk_rgb_to_hsv(rgba.get_red(), rgba.get_green(), rgba.get_blue(), &h, &s, &v); return h; } double Gobby::HueButton::get_saturation() const { return m_saturation; } double Gobby::HueButton::get_value() const { return m_value; } void Gobby::HueButton::set_hue(double hue) { double r, g, b; gtk_hsv_to_rgb(hue, m_saturation, m_value, &r, &g, &b); Gdk::RGBA rgba; rgba.set_rgba(r, g, b, 1.0); set_rgba(rgba); } void Gobby::HueButton::set_saturation(double saturation) { m_saturation = saturation; set_hue(get_hue()); // Update view } void Gobby::HueButton::set_value(double value) { m_value = value; set_hue(get_hue()); // Update view } void Gobby::HueButton::on_clicked() { if(!m_dialog.get()) { Gtk::Window* parent = NULL; Gtk::Widget* toplevel_widget = get_toplevel(); if(gtk_widget_is_toplevel(toplevel_widget->gobj())) parent = dynamic_cast(toplevel_widget); if(parent == NULL) { g_warning("Gobby::HueButton::on_clicked: " "No toplevel widget found"); return; } m_dialog.reset(new Gtk::Dialog(get_title(), *parent)); m_hue_chooser = inf_text_gtk_hue_chooser_new_with_hue(get_hue()); gtk_box_pack_start(GTK_BOX(m_dialog->get_vbox()->gobj()), m_hue_chooser, FALSE, FALSE, 0); gtk_widget_show(m_hue_chooser); m_dialog->add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL); m_dialog->add_button(_("_Ok"), Gtk::RESPONSE_OK); m_dialog->set_default_response(Gtk::RESPONSE_OK); m_dialog->set_resizable(false); m_dialog->signal_response().connect( sigc::mem_fun(*this, &HueButton::on_dialog_response)); } else { inf_text_gtk_hue_chooser_set_hue( INF_TEXT_GTK_HUE_CHOOSER(m_hue_chooser), get_hue()); } m_dialog->present(); } void Gobby::HueButton::on_parent_hide() { // Hide child dialog when the window containing the color button // goes away m_dialog.reset(NULL); } void Gobby::HueButton::on_dialog_response(int response_id) { if(response_id == Gtk::RESPONSE_OK) { set_hue( inf_text_gtk_hue_chooser_get_hue( INF_TEXT_GTK_HUE_CHOOSER(m_hue_chooser))); } m_dialog->hide(); } code/core/huebutton.hpp000066400000000000000000000033661304552132100154520ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_HUE_BUTTON_HPP_ #define _GOBBY_HUE_BUTTON_HPP_ #include #include #include #include namespace Gobby { // TODO: This should go to libinftextgtk as InfTextGtkHueButton, // inheriting directly from GtkButton to provide a clean API. class HueButton: public Gtk::ColorButton { public: HueButton(GtkColorButton* cobject, const Glib::RefPtr& builder); HueButton(const Glib::ustring& title); double get_hue() const; double get_saturation() const; double get_value() const; void set_hue(double hue); void set_saturation(double saturation); void set_value(double value); protected: virtual void on_clicked(); void on_parent_hide(); void on_dialog_response(int response_id); std::unique_ptr m_dialog; GtkWidget* m_hue_chooser; double m_saturation; double m_value; }; } #endif // _GOBBY_HUE_BUTTON_HPP_ code/core/knownhoststorage.cpp000066400000000000000000000140301304552132100170350ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "core/knownhoststorage.hpp" #include "util/file.hpp" #include "features.hpp" #include #include #include #include namespace { struct HostInfo { std::string name; std::string hostname; std::string service; }; bool load_host(xmlpp::Element* node, HostInfo& info) { bool found_name = false; bool found_hostname = false; info.service = Glib::ustring::compose( "%1", inf_protocol_get_default_port()); xmlpp::Node::NodeList list = node->get_children(); for(xmlpp::Node::NodeList::iterator iter = list.begin(); iter != list.end(); ++ iter) { xmlpp::Element* child = dynamic_cast(*iter); if(child == NULL) continue; xmlpp::TextNode* text = child->get_child_text(); if(text == NULL) continue; if(child->get_name() == "name") { info.name = text->get_content(); found_name = true; } else if(child->get_name() == "hostname") { info.hostname = text->get_content(); found_hostname = true; } else if(child->get_name() == "service") { info.service = text->get_content(); } } return found_name && found_hostname; } // Location to store the hosts file: std::string filename() { return Gobby::config_filename("hosts.xml"); } } Gobby::KnownHostStorage::KnownHostStorage(Browser& browser): m_browser(browser) { xmlpp::DomParser parser; try { parser.parse_file(filename()); } catch(xmlpp::exception& e) { // Could not open file, or file is invalid. Start // with a single entry. std::string service = Glib::ustring::compose( "%1", inf_protocol_get_default_port()); browser.add_remote("gobby.0x539.de", service, 0, false); } try { xmlpp::Document* document = parser.get_document(); if(document) { xmlpp::Element* root = document->get_root_node(); if(root) { xmlpp::Node::NodeList list = root->get_children(); for(xmlpp::Node::NodeList::iterator iter = list.begin(); iter != list.end(); ++ iter) { xmlpp::Element* child = dynamic_cast( *iter); if(child == NULL) continue; HostInfo info; if(child->get_name() == "host" && load_host(child, info)) { // TODO: Store device name // in the file as well so // that we can recover IPv6 // link-local connections. browser.add_remote( info.hostname, info.service, 0, false); } } } } } catch(xmlpp::exception& e) { // Could not read file, ignore } } Gobby::KnownHostStorage::~KnownHostStorage() { try { create_directory_with_parents( Glib::path_get_dirname(filename()), 0700); xmlpp::Document document; xmlpp::Element* root = document.create_root_node("hosts"); GtkTreeModel* model = GTK_TREE_MODEL(m_browser.get_store()); GtkTreeIter iter; for(gboolean have_item = gtk_tree_model_get_iter_first(model, &iter); have_item == TRUE; have_item = gtk_tree_model_iter_next(model, &iter)) { InfBrowser* browser; InfDiscovery* discovery; gtk_tree_model_get( model, &iter, INF_GTK_BROWSER_MODEL_COL_BROWSER, &browser, INF_GTK_BROWSER_MODEL_COL_DISCOVERY, &discovery, -1); // Skip items that were discovered if(discovery != NULL) { g_object_unref(discovery); if(browser != NULL) g_object_unref(browser); continue; } if(browser == NULL) continue; if(!INFC_IS_BROWSER(browser)) { g_object_unref(browser); continue; } InfXmlConnection* connection = infc_browser_get_connection( INFC_BROWSER(browser)); if(connection == NULL || !INF_IS_XMPP_CONNECTION(connection)) { g_object_unref(browser); continue; } InfTcpConnection* tcp; g_object_get( G_OBJECT(connection), "tcp-connection", &tcp, NULL); g_object_unref(browser); InfNameResolver* resolver; g_object_get( G_OBJECT(tcp), "resolver", &resolver, NULL); g_object_unref(tcp); // TODO: If resolver is NULL, should we instead // record hostname and port number? if(resolver == NULL) continue; const char* hostname = inf_name_resolver_get_hostname(resolver); const char* service = inf_name_resolver_get_service(resolver); const char* srv = inf_name_resolver_get_srv(resolver); if(strcmp(srv, "_infinote._tcp") != 0) { g_object_unref(resolver); continue; } gchar* name; gtk_tree_model_get( model, &iter, INF_GTK_BROWSER_MODEL_COL_NAME, &name, -1); xmlpp::Element* child = root->add_child("host"); xmlpp::Element* name_elem = child->add_child("name"); name_elem->set_child_text(name); xmlpp::Element* hostname_elem = child->add_child("hostname"); hostname_elem->set_child_text(hostname); xmlpp::Element* service_elem = child->add_child("service"); service_elem->set_child_text(service); g_object_unref(resolver); } document.write_to_file_formatted(filename()); } catch(Glib::Exception& e) { g_warning("Could not write hosts file: %s", e.what().c_str()); } catch(std::exception& e) { g_warning("Could not write hosts file: %s", e.what()); } } code/core/knownhoststorage.hpp000066400000000000000000000024351304552132100170500ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_KNOWN_HOST_STORAGE_HPP_ #define _GOBBY_KNOWN_HOST_STORAGE_HPP_ #include "core/browser.hpp" // This class stores the connection parameters of all connections on // shutdown, and on startup reads them back in and creates connection items // in the browser. namespace Gobby { class KnownHostStorage { public: KnownHostStorage(Browser& browser); ~KnownHostStorage(); protected: Browser& m_browser; }; } #endif // _GOBBY_KNOWN_HOST_STORAGE_HPP_ code/core/menumanager.cpp000066400000000000000000000074531304552132100157300ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "menumanager.hpp" #include #include namespace { bool language_sort_func(GtkSourceLanguage* lang1, GtkSourceLanguage* lang2) { // TODO: Speedup by using collation keys? // We should profile first. gchar* casefold1 = g_utf8_casefold( gtk_source_language_get_name(lang1), -1); gchar* casefold2 = g_utf8_casefold( gtk_source_language_get_name(lang2), -1); int ret = g_utf8_collate(casefold1, casefold2); g_free(casefold1); g_free(casefold2); return ret < 0; } } // anonymous namespace Gobby::MenuManager::MenuManager(GtkSourceLanguageManager* language_manager) { Glib::RefPtr builder = Gtk::Builder::create_from_resource( "/de/0x539/gobby/ui/menu.ui"); m_app_menu = Glib::RefPtr::cast_dynamic( builder->get_object("appmenu")); m_menu = Glib::RefPtr::cast_dynamic( builder->get_object("winmenu")); Glib::RefPtr highlight_mode_menu = get_highlight_mode_menu(); const gchar* const* language_ids = gtk_source_language_manager_get_language_ids( language_manager); if(language_ids != NULL) { typedef std::list LanguageList; typedef std::map LanguageMap; LanguageMap languages; for(const gchar* const* id = language_ids; *id != NULL; ++id) { GtkSourceLanguage* language = gtk_source_language_manager_get_language( language_manager, *id); if(gtk_source_language_get_hidden(language)) continue; const std::string section = gtk_source_language_get_section(language); languages[section].push_back(language); } for(LanguageMap::iterator iter = languages.begin(); iter != languages.end(); ++iter) { Glib::RefPtr submenu(Gio::Menu::create()); LanguageList& list = iter->second; list.sort(language_sort_func); for(LanguageList::const_iterator liter = list.begin(); liter != list.end(); ++liter) { GtkSourceLanguage* language = *liter; const std::string id = gtk_source_language_get_id(language); const std::string name = gtk_source_language_get_name(language); Glib::RefPtr item( Gio::MenuItem::create( name, Glib::ustring::compose( "win.highlight-mode" "('%1')", id))); submenu->append_item(item); } highlight_mode_menu->append_submenu( iter->first, submenu); } } } Glib::RefPtr Gobby::MenuManager::get_highlight_mode_menu() { // TODO: Is there a better way, like finding this by some // sort of ID? // TODO: This should be failsafe Glib::RefPtr view_menu = m_menu->get_item_link(2, Gio::MENU_LINK_SUBMENU); Glib::RefPtr last_section = view_menu->get_item_link( view_menu->get_n_items() - 1, Gio::MENU_LINK_SECTION); Glib::RefPtr highlight_mode = last_section->get_item_link(0, Gio::MENU_LINK_SUBMENU); return Glib::RefPtr::cast_dynamic(highlight_mode); } code/core/menumanager.hpp000066400000000000000000000026331304552132100157300ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_MENUMANAGER_HPP_ #define _GOBBY_MENUMANAGER_HPP_ #include #include #include namespace Gobby { class MenuManager { public: MenuManager(GtkSourceLanguageManager* language_manager); Glib::RefPtr get_app_menu() { return m_app_menu; } Glib::RefPtr get_menu() { return m_menu; } protected: Glib::RefPtr m_app_menu; Glib::RefPtr m_menu; private: Glib::RefPtr get_highlight_mode_menu(); }; } #endif // _GOBBY_MENUMARAGER_HPP_ code/core/nodewatch.cpp000066400000000000000000000050531304552132100153770ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "core/nodewatch.hpp" Gobby::NodeWatch::NodeWatch(InfBrowser* browser, const InfBrowserIter* iter): m_browser(browser), m_node_removed_handler(0), m_notify_status_handler(0) { if(iter != NULL) m_iter = *iter; else m_iter.node = NULL; // TODO: (weak-)ref browser? InfBrowserStatus status; g_object_get(G_OBJECT(m_browser), "status", &status, NULL); g_assert(iter == NULL || status == INF_BROWSER_OPEN); if(m_iter.node != NULL) { m_notify_status_handler = g_signal_connect( browser, "notify::status", G_CALLBACK(on_notify_status_static), this); m_node_removed_handler = g_signal_connect( m_browser, "node-removed", G_CALLBACK(on_node_removed_static), this); } } Gobby::NodeWatch::~NodeWatch() { if(m_browser != NULL) reset(); } void Gobby::NodeWatch::on_notify_status() { InfBrowserStatus status; g_object_get(G_OBJECT(m_browser), "status", &status, NULL); // Connection was closed: Node is no longer reachable if(status != INF_BROWSER_OPEN) { reset(); m_signal_node_removed.emit(); } } void Gobby::NodeWatch::on_node_removed(InfBrowser* browser, InfBrowserIter* iter, InfRequest* request) { g_assert(browser == m_browser); g_assert(m_iter.node != NULL); if(inf_browser_is_ancestor(m_browser, iter, &m_iter)) { reset(); m_signal_node_removed.emit(); } } void Gobby::NodeWatch::reset() { g_assert(m_browser != NULL); if(m_notify_status_handler != 0) { g_signal_handler_disconnect( m_browser, m_notify_status_handler); } if(m_node_removed_handler != 0) { g_signal_handler_disconnect( m_browser, m_node_removed_handler); } m_iter.node = NULL; m_browser = NULL; } code/core/nodewatch.hpp000066400000000000000000000047451304552132100154130ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_NODE_WATCH_HPP_ #define _GOBBY_NODE_WATCH_HPP_ #include #include #include namespace Gobby { // Weak reference on a node in a browser. If iter is null, keeps a // reference to the browser itself. class NodeWatch { public: typedef sigc::signal SignalNodeRemoved; NodeWatch(InfBrowser* browser, const InfBrowserIter* iter); ~NodeWatch(); InfBrowser* get_browser() const { return m_browser; } const InfBrowserIter* get_browser_iter() const { g_assert(m_browser); if(m_iter.node != NULL) return &m_iter; else return NULL; } SignalNodeRemoved signal_node_removed() const { return m_signal_node_removed; } protected: static void on_notify_status_static(GObject* object, GParamSpec* pspec, gpointer user_data) { static_cast(user_data)-> on_notify_status(); } static void on_node_removed_static(InfBrowser* browser, InfBrowserIter* iter, InfRequest* request, gpointer user_data) { static_cast(user_data)-> on_node_removed(browser, iter, request); } void on_notify_status(); void on_node_removed(InfBrowser* browser, InfBrowserIter* iter, InfRequest* request); void reset(); InfBrowser* m_browser; InfXmlConnection* m_connection; InfBrowserIter m_iter; gulong m_notify_status_handler; gulong m_node_removed_handler; SignalNodeRemoved m_signal_node_removed; }; } #endif // _GOBBY_NODE_WATCH_HPP_ code/core/noteplugin.cpp000066400000000000000000000132761304552132100156150ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "core/noteplugin.hpp" #include #include #include #include #include #include #include #include #include #include namespace { InfTextBuffer* make_buffer(InfUserTable* user_table) { GtkSourceBuffer* textbuffer = gtk_source_buffer_new(NULL); InfTextGtkBuffer* buffer = inf_text_gtk_buffer_new(GTK_TEXT_BUFFER(textbuffer), user_table); g_object_unref(textbuffer); return INF_TEXT_BUFFER(buffer); } InfSession* text_session_new(InfIo* io, InfCommunicationManager* manager, InfSessionStatus status, InfCommunicationGroup* sync_group, InfXmlConnection* sync_connection, const gchar* path, gpointer user_data) { InfUserTable* user_table = inf_user_table_new(); InfTextBuffer* buffer = make_buffer(user_table); InfTextSession* session = inf_text_session_new_with_user_table( manager, INF_TEXT_BUFFER(buffer), io, user_table, status, sync_group, sync_connection); g_object_unref(buffer); g_object_unref(user_table); return INF_SESSION(session); } InfSession* text_session_read(InfdStorage* storage, InfIo* io, InfCommunicationManager* manager, const gchar* path, gpointer user_data, GError** error) { InfUserTable* user_table = inf_user_table_new(); InfTextBuffer* buffer = make_buffer(user_table); const gboolean result = inf_text_filesystem_format_read( INFD_FILESYSTEM_STORAGE(storage), path, user_table, buffer, error); InfTextSession* session = NULL; if(result) { session = inf_text_session_new_with_user_table( manager, buffer, io, user_table, INF_SESSION_RUNNING, NULL, NULL); } g_object_unref(buffer); g_object_unref(user_table); return INF_SESSION(session); } gboolean text_session_write(InfdStorage* storage, InfSession* session, const gchar* path, gpointer user_data, GError** error) { return inf_text_filesystem_format_write( INFD_FILESYSTEM_STORAGE(storage), path, inf_session_get_user_table(session), INF_TEXT_BUFFER(inf_session_get_buffer(session)), error ); } InfSession* chat_session_new(InfIo* io, InfCommunicationManager* manager, InfSessionStatus status, InfCommunicationGroup* sync_group, InfXmlConnection* sync_connection, const gchar* path, gpointer user_data) { InfChatBuffer* buffer = inf_chat_buffer_new(256); InfChatSession* session = inf_chat_session_new( manager, buffer, status, sync_group, sync_connection); g_object_unref(buffer); return INF_SESSION(session); } InfSession* chat_session_read(InfdStorage* storage, InfIo* io, InfCommunicationManager* manager, const gchar* path, gpointer user_data, GError** error) { InfChatBuffer* buffer = inf_chat_buffer_new(256); const gboolean result = infd_chat_filesystem_format_read( INFD_FILESYSTEM_STORAGE(storage), path, buffer, error); InfChatSession* session = NULL; if(result) { session = inf_chat_session_new( manager, buffer, INF_SESSION_RUNNING, NULL, NULL); } g_object_unref(buffer); return INF_SESSION(session); } gboolean chat_session_write(InfdStorage* storage, InfSession* session, const gchar* path, gpointer user_data, GError** error) { return infd_chat_filesystem_format_write( INFD_FILESYSTEM_STORAGE(storage), path, INF_CHAT_BUFFER(inf_session_get_buffer(session)), error ); } const InfcNotePlugin C_TEXT_PLUGIN = { NULL, "InfText", text_session_new }; const InfcNotePlugin C_CHAT_PLUGIN = { NULL, "InfChat", chat_session_new }; const InfdNotePlugin D_TEXT_PLUGIN = { NULL, "InfdFilesystemStorage", "InfText", text_session_new, text_session_read, text_session_write }; const InfdNotePlugin D_CHAT_PLUGIN = { NULL, "InfdFilesystemStorage", "InfChat", chat_session_new, chat_session_read, chat_session_write }; } const InfcNotePlugin* Gobby::Plugins::C_TEXT = &C_TEXT_PLUGIN; const InfcNotePlugin* Gobby::Plugins::C_CHAT = &C_CHAT_PLUGIN; const InfdNotePlugin* Gobby::Plugins::D_TEXT = &D_TEXT_PLUGIN; const InfdNotePlugin* Gobby::Plugins::D_CHAT = &D_CHAT_PLUGIN; code/core/noteplugin.hpp000066400000000000000000000023321304552132100156110ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_NOTEPLUGIN_HPP_ #define _GOBBY_NOTEPLUGIN_HPP_ #include #include namespace Gobby { namespace Plugins { extern const InfcNotePlugin* C_TEXT; extern const InfcNotePlugin* C_CHAT; extern const InfdNotePlugin* D_TEXT; extern const InfdNotePlugin* D_CHAT; } } #endif // _GOBBY_NOTEPLUGIN_HPP_ code/core/preferences.cpp000066400000000000000000000112261304552132100157230ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "features.hpp" #include "core/preferences.hpp" #include "util/file.hpp" #include #include #include #include Gobby::Preferences::User::User( const Glib::RefPtr& settings, Config::ParentEntry& entry) : name(settings, entry, "name"), hue(settings, entry, "hue"), alpha(settings, entry, "alpha"), show_remote_cursors(settings, entry, "show-remote-cursors"), show_remote_selections(settings, entry, "show-remote-selections"), show_remote_current_lines( settings, entry, "show-remote-current-lines"), show_remote_cursor_positions( settings, entry, "show-remote-cursor-positions"), allow_remote_access(settings, entry, "allow-remote-access"), require_password(settings, entry, "require-password"), password(settings, entry, "password"), port(settings, entry, "port"), keep_local_documents(settings, entry, "keep-local-documents"), host_directory(settings, entry, "host-directory") { if(name.is_default()) name = Glib::get_user_name(); if(hue.is_default()) hue = Glib::Rand().get_double(); if(host_directory.is_default()) host_directory = config_filename("local-documents"); } Gobby::Preferences::Editor::Editor( const Glib::RefPtr& settings, Config::ParentEntry& entry) : tab_width(settings, entry, "tab-width"), tab_spaces(settings, entry, "tab-insert-spaces"), indentation_auto(settings, entry, "auto-indentation"), homeend_smart(settings, entry, "smart-homeend"), autosave_enabled(settings, entry, "autosave-enabled"), autosave_interval(settings, entry, "autosave-interval") { } Gobby::Preferences::View::View( const Glib::RefPtr& settings, Config::ParentEntry& entry) : wrap_mode(settings, entry, "wrap-mode"), linenum_display(settings, entry, "display-line-numbers"), curline_highlight(settings, entry, "highlight-current-line"), margin_display(settings, entry, "margin-display"), margin_pos(settings, entry, "margin-position"), bracket_highlight(settings, entry, "highlight-matching-brackets"), whitespace_display(settings, entry, "display-whitespace") { } Gobby::Preferences::Appearance::Appearance( const Glib::RefPtr& settings, Config::ParentEntry& entry) : toolbar_style(settings, entry, "toolbar-style"), font(settings, entry, "font"), scheme_id(settings, entry, "scheme-id"), show_toolbar(settings, entry, "show-toolbar"), show_statusbar(settings, entry, "show-statusbar"), show_browser(settings, entry, "show-browser"), show_chat(settings, entry, "show-chat"), show_document_userlist(settings, entry, "show-document-userlist"), show_chat_userlist(settings, entry, "show-chat-userlist") { } Gobby::Preferences::Security::Security( const Glib::RefPtr& settings, Config::ParentEntry& entry) : use_system_trust(settings, entry, "use-system-trust"), trusted_cas(settings, entry, "trusted-cas"), policy(settings, entry, "policy"), authentication_enabled(settings, entry, "authentication-enabled"), certificate_file(settings, entry, "certificate-file"), key_file(settings, entry, "key-file") { } Gobby::Preferences::Network::Network( const Glib::RefPtr& settings, Config::ParentEntry& entry) : keepalive(settings, entry, "keepalive") { } Gobby::Preferences::Preferences(Config& config): m_settings(Gio::Settings::create("de.0x539.gobby.preferences")), user(m_settings->get_child("user"), config.get_root()["user"]), editor(m_settings->get_child("editor"), config.get_root()["editor"]), view(m_settings->get_child("view"), config.get_root()["view"]), appearance(m_settings->get_child("appearance"), config.get_root()["appearance"]), security(m_settings->get_child("security"), config.get_root()["security"]), network(m_settings->get_child("network"), config.get_root()["network"]) { } code/core/preferences.hpp000066400000000000000000000374411304552132100157370ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_PREFERENCES_HPP_ #define _GOBBY_PREFERENCES_HPP_ #include "util/config.hpp" #include "util/i18n.hpp" #include "features.hpp" #include #include #include #include #include #include namespace Gobby { template struct SettingTraits { inline static Type get(const Glib::RefPtr& settings, const Glib::ustring& key) { Glib::Variant base; settings->get_value(key, base); return Type(base.get()); } inline static bool get_from_entry(const Config::ParentEntry& entry, const Glib::ustring& key, Type& value) { if(entry.has_value(key)) { value = entry.get_value(key); return true; } return false; } inline static void set(const Glib::RefPtr& settings, const Glib::ustring& key, const Type& value) { Glib::Variant var(Glib::Variant::create(value)); settings->set_value(key, var); } inline static bool equals(const Type& val1, const Type& val2) { return val1 == val2; } }; // This cannot go through the generic case defined above because // Glib::Variant would map to a byte array, not a string. template<> struct SettingTraits { inline static std::string get(const Glib::RefPtr& settings, const Glib::ustring& key) { return settings->get_string(key); } inline static bool get_from_entry(const Config::ParentEntry& entry, const Glib::ustring& key, std::string& value) { if(entry.has_value(key)) { value = entry.get_value(key); return true; } return false; } inline static void set(const Glib::RefPtr& settings, const Glib::ustring& key, const std::string& value) { settings->set_string(key, value); } inline static bool equals(const std::string& val1, const std::string& val2) { return val1 == val2; } }; template<> struct SettingTraits { inline static Pango::FontDescription get(const Glib::RefPtr& settings, const Glib::ustring& key) { return Pango::FontDescription( settings->get_string(key)); } inline static bool get_from_entry(const Config::ParentEntry& entry, const Glib::ustring& key, Pango::FontDescription& value) { if(entry.has_value(key)) { value = Pango::FontDescription( entry.get_value(key)); return true; } return false; } inline static void set(const Glib::RefPtr& settings, const Glib::ustring& key, const Pango::FontDescription& value) { settings->set_string(key, value.to_string()); } inline static bool equals(const Pango::FontDescription& val1, const Pango::FontDescription& val2) { return val1 == val2; } }; template<> struct SettingTraits { inline static InfKeepalive get(const Glib::RefPtr& settings, const Glib::ustring& key) { // Decode (asbuu) Glib::VariantBase base; settings->get_value(key, base); Glib::VariantContainerBase container = Glib::VariantBase::cast_dynamic< Glib::VariantContainerBase>(base); Glib::Variant > mask = Glib::VariantBase::cast_dynamic< Glib::Variant > >( container.get_child(0)); Glib::Variant enabled = Glib::VariantBase::cast_dynamic< Glib::Variant >( container.get_child(1)); Glib::Variant interval = Glib::VariantBase::cast_dynamic< Glib::Variant >( container.get_child(2)); Glib::Variant time = Glib::VariantBase::cast_dynamic< Glib::Variant >( container.get_child(3)); InfKeepalive keepalive; keepalive.mask = static_cast(0); std::vector mask_values = mask.get(); GFlagsClass* klass = G_FLAGS_CLASS( g_type_class_ref(INF_TYPE_KEEPALIVE_MASK)); for(std::vector::const_iterator iter = mask_values.begin(); iter != mask_values.end(); ++iter) { guint i; for(i = 0; i < klass->n_values; ++i) { const int is_all = strcmp( klass->values[i].value_nick, "all"); if(is_all == 0) continue; if(*iter == klass->values[i].value_nick) break; } if(i == klass->n_values) { g_type_class_unref(klass); throw std::runtime_error( Glib::ustring::compose( _("'%1' is not a valid " "keepalive mask value"), *iter)); } keepalive.mask = static_cast( static_cast(keepalive.mask) | klass->values[i].value); } g_type_class_unref(klass); keepalive.enabled = enabled.get(); keepalive.time = time.get(); keepalive.interval = interval.get(); return keepalive; } inline static bool get_from_entry(const Config::ParentEntry& entry, const Glib::ustring& key, InfKeepalive& value) { bool result = false; if(entry.has_value(key + "-mask")) { value.mask = static_cast( entry.get_value(key + "-mask")); result = true; } if(entry.has_value(key + "-enabled")) { value.enabled = entry.get_value( key + "-enabled"); result = true; } if(entry.has_value(key + "-time")) { value.time = entry.get_value( key + "-time"); result = true; } if(entry.has_value(key + "-interval")) { value.interval = entry.get_value( key + "-interval"); result = true; } return result; } inline static void set(const Glib::RefPtr& settings, const Glib::ustring& key, const InfKeepalive& value) { std::vector mask_values; GFlagsClass* klass = G_FLAGS_CLASS( g_type_class_ref(INF_TYPE_KEEPALIVE_MASK)); for(guint i = 0; i < klass->n_values; ++i) { if(strcmp(klass->values[i].value_nick, "all") == 0) continue; const guint mask = static_cast(value.mask); if(mask & klass->values[i].value) { mask_values.push_back( klass->values[i].value_nick); } } g_type_class_unref(klass); Glib::Variant > mask = Glib::Variant >::create( mask_values); Glib::Variant enabled = Glib::Variant::create(value.enabled); Glib::Variant time = Glib::Variant::create(value.time); Glib::Variant interval = Glib::Variant::create(value.interval); std::vector tuple_children; tuple_children.push_back(mask); tuple_children.push_back(enabled); tuple_children.push_back(time); tuple_children.push_back(interval); Glib::VariantContainerBase entry = Glib::VariantContainerBase::create_tuple( tuple_children); settings->set_value(key, entry); } inline static bool equals(const InfKeepalive& val1, const InfKeepalive& val2) { return val1.mask == val2.mask && val1.enabled == val2.enabled && val1.time == val2.time && val1.interval == val2.interval; } }; template struct SettingTraitsEnum { inline static EnumType get(const Glib::RefPtr& settings, const Glib::ustring& key) { return static_cast(settings->get_enum(key)); } inline static bool get_from_entry(const Config::ParentEntry& entry, const Glib::ustring& key, EnumType& value) { if(entry.has_value(key)) { value = static_cast( entry.get_value(key)); return true; } return false; } inline static void set(const Glib::RefPtr& settings, const Glib::ustring& key, const EnumType& value) { g_settings_set_enum( settings->gobj(), key.c_str(), static_cast(value)); } inline static bool equals(const EnumType& val1, const EnumType& val2) { return val1 == val2; } }; template struct SettingTraitsFlags { inline static FlagsType get(const Glib::RefPtr& settings, const Glib::ustring& key) { return static_cast(settings->get_flags(key)); } inline static bool get_from_entry(const Config::ParentEntry& entry, const Glib::ustring& key, FlagsType& value) { if(entry.has_value(key)) { value = static_cast( entry.get_value(key)); return true; } return false; } inline static void set(const Glib::RefPtr& settings, const Glib::ustring& key, const FlagsType& value) { g_settings_set_flags( settings->gobj(), key.c_str(), static_cast(value)); } inline static bool equals(const FlagsType& val1, const FlagsType& val2) { return val1 == val2; } }; template<> struct SettingTraits: SettingTraitsEnum {}; template<> struct SettingTraits: SettingTraitsEnum {}; template<> struct SettingTraits: SettingTraitsEnum {}; template<> struct SettingTraits: SettingTraitsFlags {}; class Preferences { public: // TODO: This class should handle writability template class Option { public: typedef sigc::signal signal_changed_type; Option(const Type& initial_value): m_value(initial_value), m_changed_handler(0) {} // TODO: Remove this constructor Option(const Glib::RefPtr& settings, const Glib::ustring& key): m_settings(settings), m_key(key), m_value(SettingTraits::get(settings, key)) { m_changed_handler = g_signal_connect( G_OBJECT(settings->gobj()), Glib::ustring::compose( "changed::%1", key).c_str(), G_CALLBACK(on_changed_static), this); } Option(const Glib::RefPtr& settings, const Config::ParentEntry& entry, const Glib::ustring& key): m_settings(settings), m_key(key), m_value(SettingTraits::get(settings, key)) { m_changed_handler = g_signal_connect( G_OBJECT(settings->gobj()), Glib::ustring::compose( "changed::%1", key).c_str(), G_CALLBACK(on_changed_static), this); if(SettingTraits::get_from_entry(entry, key, m_value)) set_settings(); } ~Option() { if(m_changed_handler != 0) { g_signal_handler_disconnect( m_settings->gobj(), m_changed_handler); } } bool is_default() const { Glib::VariantBase current_variant; Glib::VariantBase default_variant; m_settings->get_value(m_key, current_variant); m_settings->get_default_value(m_key, default_variant); return current_variant.equal(default_variant); } const Option& operator=(const Type& new_value) { m_value = new_value; notify(); return *this; } void set(const Type& new_value) { *this = new_value; } const Type& get() const { return m_value; } operator const Type&() const { return m_value; } /*operator Type&() { return m_value; }*/ signal_changed_type signal_changed() const { return m_signal_changed; } private: static void on_changed_static(GSettings* settings, const gchar* key, gpointer user_data) { static_cast(user_data)->on_changed(); } void on_changed() { const Type new_value( SettingTraits::get(m_settings, m_key)); if(!SettingTraits::equals(new_value, m_value)) { m_value = new_value; m_signal_changed.emit(); } } void notify() const { // Notify GSettings about the changed value set_settings(); // Notify other users about the changed value m_signal_changed.emit(); } void set_settings() const { if(m_settings) { g_signal_handler_block(m_settings->gobj(), m_changed_handler); SettingTraits::set( m_settings, m_key, m_value); g_signal_handler_unblock(m_settings->gobj(), m_changed_handler); } } protected: Glib::RefPtr m_settings; const Glib::ustring m_key; gulong m_changed_handler; Type m_value; signal_changed_type m_signal_changed; }; Preferences(Config& m_config); class User { public: User(const Glib::RefPtr& settings, Config::ParentEntry& entry); Option name; Option hue; Option alpha; Option show_remote_cursors; Option show_remote_selections; Option show_remote_current_lines; Option show_remote_cursor_positions; Option allow_remote_access; Option require_password; Option password; Option port; Option keep_local_documents; Option host_directory; }; class Editor { public: Editor(const Glib::RefPtr& settings, Config::ParentEntry& entry); Option tab_width; Option tab_spaces; Option indentation_auto; Option homeend_smart; Option autosave_enabled; Option autosave_interval; }; class View { public: View(const Glib::RefPtr& settings, Config::ParentEntry& entry); Option wrap_mode; Option linenum_display; Option curline_highlight; Option margin_display; Option margin_pos; Option bracket_highlight; Option whitespace_display; }; class Appearance { public: Appearance(const Glib::RefPtr& settings, Config::ParentEntry& entry); // TODO: Option use_system_default_toolbar_style // (sets toolbar_style to default). Option toolbar_style; Option font; Option scheme_id; Option show_toolbar; Option show_statusbar; Option show_browser; Option show_chat; Option show_document_userlist; Option show_chat_userlist; }; class Security { public: Security(const Glib::RefPtr& settings, Config::ParentEntry& entry); Option use_system_trust; Option trusted_cas; Option policy; Option authentication_enabled; Option key_file; Option certificate_file; }; class Network { public: Network(const Glib::RefPtr& settings, Config::ParentEntry& entry); Option keepalive; }; private: Glib::RefPtr m_settings; public: User user; Editor editor; View view; Appearance appearance; Security security; Network network; }; template std::ostream& operator<<(std::ostream& stream, const Preferences::Option& option) { stream << static_cast(option); return stream; } } #endif // _GOBBY_PREFERENCES_HPP_ code/core/selfhoster.cpp000066400000000000000000000220771304552132100156060ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "core/selfhoster.hpp" #include "util/i18n.hpp" #include Gobby::SelfHoster::SelfHoster(InfIo* io, InfCommunicationManager* communication_manager, InfLocalPublisher* publisher, InfSaslContext* sasl_context, StatusBar& status_bar, CertificateManager& cert_manager, const Preferences& preferences): m_sasl_context(sasl_context), m_status_bar(status_bar), m_cert_manager(cert_manager), m_preferences(preferences), m_dh_params_loaded(false), m_info_handle(status_bar.invalid_handle()), m_dh_params_message_handle(status_bar.invalid_handle()), m_directory(infd_directory_new(io, NULL, communication_manager)), m_server(io, publisher) { inf_sasl_context_ref(m_sasl_context); if(m_preferences.user.keep_local_documents) { const std::string directory = m_preferences.user.host_directory; InfdFilesystemStorage* storage = infd_filesystem_storage_new(directory.c_str()); g_object_set(G_OBJECT(m_directory), "storage", storage, NULL); g_object_unref(storage); } InfdServerPool* pool = infd_server_pool_new(m_directory); m_server.set_pool(pool); g_object_unref(pool); m_preferences.user.require_password.signal_changed().connect( sigc::mem_fun( *this, &SelfHoster::on_require_password_changed)); /*m_preferences.user.password.signal_changed().connect( sigc::mem_fun(*this, &SelfHoster::on_password));*/ m_preferences.user.allow_remote_access.signal_changed().connect( sigc::mem_fun(*this, &SelfHoster::apply_preferences)); m_preferences.user.port.signal_changed().connect( sigc::mem_fun(*this, &SelfHoster::apply_preferences)); m_preferences.user.keep_local_documents.signal_changed().connect( sigc::mem_fun(*this, &SelfHoster::apply_preferences)); m_preferences.user.host_directory.signal_changed().connect( sigc::mem_fun(*this, &SelfHoster::apply_preferences)); m_preferences.security.authentication_enabled.signal_changed(). connect(sigc::mem_fun(*this, &SelfHoster::apply_preferences)); m_preferences.security.policy.signal_changed().connect( sigc::mem_fun(*this, &SelfHoster::apply_preferences)); m_preferences.network.keepalive.signal_changed().connect( sigc::mem_fun(*this, &SelfHoster::apply_preferences)); m_cert_manager.signal_credentials_changed().connect( sigc::mem_fun(*this, &SelfHoster::apply_preferences)); apply_preferences(); } Gobby::SelfHoster::~SelfHoster() { g_object_unref(m_directory); inf_sasl_context_unref(m_sasl_context); } bool Gobby::SelfHoster::ensure_dh_params() { // If they are loaded already: perfect. Note this does not mean we // actually have them, this flag is also set if we attempted to // generate the parameters but the generation failed. if(m_dh_params_loaded) return true; // Load from certificate manager, if available if(m_cert_manager.get_dh_params() != NULL) { m_dh_params_loaded = true; return true; } // Otherwise go and create a new set of parameters if(m_dh_params_handle.get() == NULL) { m_dh_params_message_handle = m_status_bar.add_info_message( _("Generating 2048-bit Diffie-Hellman " "parameters...")); m_dh_params_handle = create_dh_params( 2048, sigc::mem_fun(*this, &SelfHoster::on_dh_params_done)); } return false; } void Gobby::SelfHoster::on_dh_params_done(const DHParamsGeneratorHandle* hndl, gnutls_dh_params_t dh_params, const GError* error) { g_assert(m_dh_params_message_handle != m_status_bar.invalid_handle()); m_status_bar.remove_message(m_dh_params_message_handle); // Set this flag also when an error occured, to prevent trying to // re-generate the parameters all the time. m_dh_params_loaded = true; if(dh_params != NULL) { // Set the DH parameters in the certificate manager: this // will cause a credentials_changed notification, and we'll // retry starting the server. m_cert_manager.set_dh_params(dh_params); } else { m_status_bar.add_error_message( _("Failed to generate Diffie-Hellman parameters"), Glib::ustring::compose( _("This means that Perfect Forward Secrecy " "(PFS) is not available. Restart Gobby to " "re-try generating the parameters. The " "specific error was:\n\n%1"), error->message)); } } void Gobby::SelfHoster::directory_foreach_func_close_static( InfXmlConnection* connection, gpointer user_data) { inf_xml_connection_close(connection); } void Gobby::SelfHoster::directory_foreach_func_set_sasl_context_static( InfXmlConnection* connection, gpointer user_data) { g_assert(INF_IS_XMPP_CONNECTION(connection)); SelfHoster* hoster = static_cast(user_data); inf_xmpp_connection_reset_sasl_authentication( INF_XMPP_CONNECTION(connection), hoster->m_sasl_context, hoster->get_sasl_mechanisms()); } const char* Gobby::SelfHoster::get_sasl_mechanisms() const { if(m_preferences.user.require_password) return "PLAIN"; else return "ANONYMOUS"; } void Gobby::SelfHoster::on_require_password_changed() { // Update SASL context and mechanisms for new connections: m_server.set_sasl_context(m_sasl_context, get_sasl_mechanisms()); // Also update the SASL context for all existing connections. This is // important, so that the new password requirement setting also // affects already connected but not yet authorized clients. infd_directory_foreach_connection( m_directory, directory_foreach_func_set_sasl_context_static, this); } void Gobby::SelfHoster::apply_preferences() { // Update directory storage if(m_preferences.user.keep_local_documents) { InfdStorage* storage = infd_directory_get_storage(m_directory); g_assert(storage == NULL || INFD_IS_FILESYSTEM_STORAGE(storage)); InfdFilesystemStorage* fs_storage = INFD_FILESYSTEM_STORAGE(storage); const std::string new_directory = m_preferences.user.host_directory; bool set_new_storage = true; if(fs_storage != NULL) { gchar* root_directory; g_object_get( G_OBJECT(fs_storage), "root-directory", &root_directory, NULL); if(strcmp(root_directory, new_directory.c_str()) == 0) set_new_storage = false; g_free(root_directory); } if(set_new_storage) { fs_storage = infd_filesystem_storage_new( new_directory.c_str()); g_object_set( G_OBJECT(m_directory), "storage", fs_storage, NULL); g_object_unref(fs_storage); } } else { if(infd_directory_get_storage(m_directory) != NULL) { g_object_set( G_OBJECT(m_directory), "storage", NULL, NULL); } } // Remove old statusbar message, if any if(m_info_handle != m_status_bar.invalid_handle()) { m_status_bar.remove_message(m_info_handle); m_info_handle = m_status_bar.invalid_handle(); } // Close server and all connections if no access is required if(!m_preferences.user.allow_remote_access) { infd_directory_foreach_connection( m_directory, directory_foreach_func_close_static, this); if(m_server.is_open()) m_server.close(); return; } // Okay, we want to share our documents, so let's try to start a // server for it. // Make sure TLS credentials are available. if(m_preferences.security.policy != INF_XMPP_CONNECTION_SECURITY_ONLY_UNSECURED && (m_preferences.security.authentication_enabled != true || m_cert_manager.get_private_key() == NULL || m_cert_manager.get_certificates() == NULL)) { m_info_handle = m_status_bar.add_info_message( _("In order to start sharing your documents, " "choose a private key and certificate or " "create a new pair in the preferences")); return; } // Make sure we have DH parameters if(!ensure_dh_params()) return; // Okay, go and open a server. If the server is already open the // command below will only change the port and/or security policy. try { const InfKeepalive& keepalive = m_preferences.network.keepalive; m_server.open(m_preferences.user.port, &keepalive, m_preferences.security.policy, m_cert_manager.get_credentials(), m_sasl_context, get_sasl_mechanisms()); } catch(const std::exception& ex) { m_status_bar.add_error_message(_("Failed to share documents"), ex.what()); return; } } code/core/selfhoster.hpp000066400000000000000000000051301304552132100156020ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_SELF_HOSTER_HPP_ #define _GOBBY_SELF_HOSTER_HPP_ #include "core/credentialsgenerator.hpp" #include "core/certificatemanager.hpp" #include "core/statusbar.hpp" #include "core/server.hpp" #include #include #include #include #include namespace Gobby { class SelfHoster: public sigc::trackable { public: SelfHoster(InfIo* io, InfCommunicationManager* communication_manager, InfLocalPublisher* publisher, InfSaslContext* sasl_context, StatusBar& status_bar, CertificateManager& cert_manager, const Preferences& preferences); ~SelfHoster(); InfdDirectory* get_directory() { return m_directory; } protected: static void directory_foreach_func_close_static( InfXmlConnection* connection, gpointer user_data); static void directory_foreach_func_set_sasl_context_static( InfXmlConnection* connection, gpointer user_data); const char* get_sasl_mechanisms() const; bool ensure_dh_params(); void on_dh_params_done(const DHParamsGeneratorHandle* handle, gnutls_dh_params_t dh_params, const GError* error); void on_require_password_changed(); void apply_preferences(); InfSaslContext* m_sasl_context; StatusBar& m_status_bar; CertificateManager& m_cert_manager; const Preferences& m_preferences; bool m_dh_params_loaded; StatusBar::MessageHandle m_info_handle; StatusBar::MessageHandle m_dh_params_message_handle; InfdDirectory* m_directory; Server m_server; std::unique_ptr m_dh_params_handle; }; } #endif // _GOBBY_SELF_HOSTER_HPP_ code/core/server.cpp000066400000000000000000000153251304552132100147340ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "core/selfhoster.hpp" #include "util/file.hpp" #include "util/i18n.hpp" #include #include #include #include Gobby::Server::Server(InfIo* io, InfLocalPublisher* publisher): m_io(io), m_publisher(publisher), m_xmpp4(NULL), m_xmpp6(NULL), m_pool(NULL) { g_object_ref(m_io); } Gobby::Server::~Server() { if(is_open()) close(); set_pool(NULL); g_object_unref(m_io); } void Gobby::Server::open(unsigned int port, const InfKeepalive* keepalive, InfXmppConnectionSecurityPolicy security_policy, InfCertificateCredentials* creds, InfSaslContext* context, const char* sasl_mechanisms) { // If we can open one of tcp4 or tcp6 that's a success. InfdTcpServer* tcp4; InfdTcpServer* tcp6; // If the server is already open and we do not need to change the // port, then just change the credentials and SASL context without // doing anything else. if(is_open() && get_port() == port) { set_keepalive(keepalive); set_credentials(security_policy, creds); set_sasl_context(context, sasl_mechanisms); return; } static const guint8 ANY6_ADDR[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; InfIpAddress* any6 = inf_ip_address_new_raw6(ANY6_ADDR); tcp4 = INFD_TCP_SERVER(g_object_new( INFD_TYPE_TCP_SERVER, "io", m_io, "local-address", NULL, "local-port", port, NULL)); tcp6 = INFD_TCP_SERVER(g_object_new( INFD_TYPE_TCP_SERVER, "io", m_io, "local-address", any6, "local-port", port, NULL)); inf_ip_address_free(any6); if(!infd_tcp_server_open(tcp6, NULL)) { g_object_unref(tcp6); tcp6 = NULL; GError* error = NULL; if(!infd_tcp_server_open(tcp4, &error)) { g_object_unref(tcp4); const std::string message = error->message; g_error_free(error); throw std::runtime_error(message); } } else { if(!infd_tcp_server_open(tcp4, NULL)) { g_object_unref(tcp4); tcp4 = NULL; } } // We have the new server open, from this point on there is nothing // that can go wrong anymore. Therefore, close the old server and // take over the new one. if(is_open()) close(); InfXmppConnectionSecurityPolicy policy = INF_XMPP_CONNECTION_SECURITY_ONLY_UNSECURED; if(creds != NULL) policy = security_policy; if(tcp4) { infd_tcp_server_set_keepalive(tcp4, keepalive); m_xmpp4 = infd_xmpp_server_new( tcp4, policy, creds, context, sasl_mechanisms); g_object_unref(tcp4); } if(tcp6) { infd_tcp_server_set_keepalive(tcp6, keepalive); m_xmpp6 = infd_xmpp_server_new( tcp6, policy, creds, context, sasl_mechanisms); g_object_unref(tcp6); } if(m_pool) { if(m_xmpp4 != NULL) { infd_server_pool_add_server( m_pool, INFD_XML_SERVER(m_xmpp4)); infd_server_pool_add_local_publisher( m_pool, m_xmpp4, m_publisher); } if(m_xmpp6 != NULL) { infd_server_pool_add_server( m_pool, INFD_XML_SERVER(m_xmpp6)); infd_server_pool_add_local_publisher( m_pool, m_xmpp6, m_publisher); } } } void Gobby::Server::close() { g_assert(is_open()); if(m_xmpp6 != NULL) { // Will be removed from server pool automatically infd_xml_server_close(INFD_XML_SERVER(m_xmpp6)); g_object_unref(m_xmpp6); m_xmpp6 = NULL; } if(m_xmpp4 != NULL) { // Will be removed from server pool automatically infd_xml_server_close(INFD_XML_SERVER(m_xmpp4)); g_object_unref(m_xmpp4); m_xmpp4 = NULL; } } bool Gobby::Server::is_open() const { return m_xmpp4 != NULL || m_xmpp6 != NULL; } unsigned int Gobby::Server::get_port() const { g_assert(is_open()); InfdXmppServer* xmpp = m_xmpp6; if(xmpp == NULL) xmpp = m_xmpp4; g_assert(xmpp != NULL); InfdTcpServer* tcp; g_object_get(G_OBJECT(xmpp), "tcp-server", &tcp, NULL); guint port; g_object_get(G_OBJECT(tcp), "local-port", &port, NULL); g_object_unref(tcp); return port; } void Gobby::Server::set_keepalive(const InfKeepalive* keepalive) { if(m_xmpp6 != NULL) { InfdTcpServer* tcp; g_object_get(G_OBJECT(m_xmpp6), "tcp-server", &tcp, NULL); infd_tcp_server_set_keepalive(tcp, keepalive); g_object_unref(tcp); } if(m_xmpp4 != NULL) { InfdTcpServer* tcp; g_object_get(G_OBJECT(m_xmpp4), "tcp-server", &tcp, NULL); infd_tcp_server_set_keepalive(tcp, keepalive); g_object_unref(tcp); } } void Gobby::Server::set_sasl_context(InfSaslContext* context, const char* sasl_mechanisms) { if(m_xmpp6 != NULL) { g_object_set( G_OBJECT(m_xmpp6), "sasl-context", context, "sasl-mechanisms", sasl_mechanisms, NULL); } if(m_xmpp4 != NULL) { g_object_set( G_OBJECT(m_xmpp4), "sasl-context", context, "sasl-mechanisms", sasl_mechanisms, NULL); } } void Gobby::Server::set_credentials(InfXmppConnectionSecurityPolicy policy, InfCertificateCredentials* credentials) { if(m_xmpp6 != NULL) { g_object_set( G_OBJECT(m_xmpp6), "security-policy", policy, "credentials", credentials, NULL); } if(m_xmpp4 != NULL) { g_object_set( G_OBJECT(m_xmpp4), "security-policy", policy, "credentials", credentials, NULL); } } void Gobby::Server::set_pool(InfdServerPool* pool) { if(m_pool != NULL) { if(m_xmpp4 != NULL) infd_server_pool_remove_server( m_pool, INFD_XML_SERVER(m_xmpp4)); if(m_xmpp6 != NULL) infd_server_pool_remove_server( m_pool, INFD_XML_SERVER(m_xmpp6)); g_object_unref(m_pool); } m_pool = pool; if(m_pool != NULL) { g_object_ref(m_pool); if(m_xmpp4 != NULL) { infd_server_pool_add_server( m_pool, INFD_XML_SERVER(m_xmpp4)); infd_server_pool_add_local_publisher( m_pool, m_xmpp4, m_publisher); } if(m_xmpp6 != NULL) { infd_server_pool_add_server( m_pool, INFD_XML_SERVER(m_xmpp6)); infd_server_pool_add_local_publisher( m_pool, m_xmpp6, m_publisher); } } } code/core/server.hpp000066400000000000000000000045061304552132100147400ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_SERVER_HPP_ #define _GOBBY_SERVER_HPP_ #include #include #include #include namespace Gobby { class Server { public: Server(InfIo* io, InfLocalPublisher* publisher); ~Server(); // Can throw; can be used on already open servers: void open(unsigned int port, const InfKeepalive* keepalive, InfXmppConnectionSecurityPolicy security_policy, InfCertificateCredentials* creds, InfSaslContext* sasl_context, const char* sasl_mechanisms); void close(); bool is_open() const; unsigned int get_port() const; // Set new keepalive settings on running server, for new connections: void set_keepalive(const InfKeepalive* keepalive); // Use new credentials on running server, for new connections: void set_credentials(InfXmppConnectionSecurityPolicy security_policy, InfCertificateCredentials* credentials); // Set SASL context, for new connections. void set_sasl_context(InfSaslContext* sasl_context, const char* m_sasl_mechanisms); // Set a server pool to which to add the servers, and which to // use for publishing with the publisher. void set_pool(InfdServerPool* pool); protected: InfIo* m_io; InfLocalPublisher* m_publisher; InfdXmppServer* m_xmpp4; InfdXmppServer* m_xmpp6; InfdServerPool* m_pool; }; } #endif // _GOBBY_SERVER_HPP_ code/core/sessionuserview.cpp000066400000000000000000000072141304552132100167010ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "core/sessionuserview.hpp" #include "core/closableframe.hpp" #include "util/i18n.hpp" // TODO: Consider using a single user list for all SessionViews, reparenting // into the current SessionUserView's frame. Keep dummy widgets in other // SessionUserViews so text does not resize. // Or, maybe more favorable, just put the userlist outside of the notebook, // but keep the notebook tabs on top of the userlist. Gobby::SessionUserView::SessionUserView(SessionView& view, bool show_disconnected, Preferences::Option& opt_view, Preferences::Option& w): m_view(view), m_userlist_width(w), m_userlist(inf_session_get_user_table(view.get_session())) { m_userlist.show(); m_userlist.set_show_disconnected(show_disconnected); Gtk::Frame* frame = Gtk::manage(new ClosableFrame( _("User List"), "user-list", opt_view)); frame->set_shadow_type(Gtk::SHADOW_IN); frame->add(m_userlist); // frame manages visibility itself pack1(view, true, false); pack2(*frame, false, false); } void Gobby::SessionUserView::on_size_allocate(Gtk::Allocation& allocation) { Gtk::HPaned::on_size_allocate(allocation); // Setup initial paned position. We can't do this simply every time // on_size_allocate() is called since this would lead to an endless // loop somehow when the userlist width is changed forcefully // (for example by a m_view.set_info() requiring much width). if(!m_doc_userlist_width_changed_connection.connected()) { Glib::SignalProxyProperty proxy = property_position().signal_changed(); m_doc_userlist_width_changed_connection = proxy.connect(sigc::mem_fun( *this, &SessionUserView:: on_doc_userlist_width_changed)); Preferences::Option& option = m_userlist_width; m_pref_userlist_width_changed_connection = option.signal_changed().connect(sigc::mem_fun( *this, &SessionUserView:: on_pref_userlist_width_changed)); int desired_position = get_width() - m_userlist_width; desired_position = std::min( desired_position, property_max_position()); if(get_position() != desired_position) set_position(desired_position); } } void Gobby::SessionUserView::on_doc_userlist_width_changed() { unsigned int userlist_width = get_width() - get_position(); if(m_userlist_width != userlist_width) { m_pref_userlist_width_changed_connection.block(); m_userlist_width = userlist_width; m_pref_userlist_width_changed_connection.unblock(); } } void Gobby::SessionUserView::on_pref_userlist_width_changed() { int position = get_width() - m_userlist_width; if(get_position() != position) { m_doc_userlist_width_changed_connection.block(); set_position(position); m_doc_userlist_width_changed_connection.unblock(); } } code/core/sessionuserview.hpp000066400000000000000000000034611304552132100167060ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_SESSIONUSERVIEW_HPP_ #define _GOBBY_SESSIONUSERVIEW_HPP_ #include "core/sessionview.hpp" #include "core/preferences.hpp" #include "core/userlist.hpp" #include // Shows a sessionview with a userlist on the right hand side of it namespace Gobby { class SessionUserView: public Gtk::HPaned { public: SessionUserView(SessionView& view, bool show_disconnected, Preferences::Option& userlist_view, Preferences::Option& userlist_width); SessionView& get_session_view() const { return m_view; } protected: virtual void on_size_allocate(Gtk::Allocation& allocation); void on_doc_userlist_width_changed(); void on_pref_userlist_width_changed(); SessionView& m_view; Preferences::Option& m_userlist_width; UserList m_userlist; sigc::connection m_doc_userlist_width_changed_connection; sigc::connection m_pref_userlist_width_changed_connection; }; } #endif // _GOBBY_SESSIONUSERVIEW_HPP_ code/core/sessionview.cpp000066400000000000000000000051771304552132100160100ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "features.hpp" #include "core/sessionview.hpp" Gobby::SessionView::SessionView(InfSession* session, const Glib::ustring& title, const Glib::ustring& path, const Glib::ustring& hostname): m_session(session), m_title(title), m_path(path), m_hostname(hostname) { g_object_ref(m_session); m_info_label.set_halign(Gtk::ALIGN_START); m_info_label.set_hexpand(true); m_info_label.set_selectable(true); m_info_label.set_line_wrap(true); m_info_label.show(); m_info_close_button.set_halign(Gtk::ALIGN_END); m_info_close_button.set_valign(Gtk::ALIGN_START); m_info_close_button.signal_clicked().connect( sigc::mem_fun(m_info_frame, &Gtk::Frame::hide)); // Don't show info close button by default m_info_grid.set_orientation(Gtk::ORIENTATION_VERTICAL); m_info_grid.set_row_spacing(6); m_info_grid.attach(m_info_close_button, 0, 0, 1, 1); m_info_grid.attach(m_info_label, 0, 1, 1, 1); m_info_grid.set_border_width(6); m_info_grid.show(); m_info_frame.set_shadow_type(Gtk::SHADOW_IN); m_info_frame.add(m_info_grid); // Don't show infoframe by default set_orientation(Gtk::ORIENTATION_VERTICAL); attach(m_info_frame, 0, 0, 1, 1); } Gobby::SessionView::~SessionView() { g_object_unref(m_session); //m_session = NULL; // TODO: Any reason to reset this? } void Gobby::SessionView::set_info(const Glib::ustring& info, bool closable) { m_info_label.set_text(info); if(closable) m_info_close_button.show(); else m_info_close_button.hide(); m_info_frame.show(); } void Gobby::SessionView::unset_info() { m_info_frame.hide(); } InfUser* Gobby::SessionView::get_active_user() const { return NULL; } void Gobby::SessionView::active_user_changed(InfUser* new_user) { m_signal_active_user_changed.emit(new_user); } code/core/sessionview.hpp000066400000000000000000000043351304552132100160100ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_SESSIONVIEW_HPP_ #define _GOBBY_SESSIONVIEW_HPP_ #include "util/closebutton.hpp" #include #include #include #include namespace Gobby { class SessionView: public Gtk::Grid { public: typedef sigc::signal SignalActiveUserChanged; SessionView(InfSession* session, const Glib::ustring& title, const Glib::ustring& path, const Glib::ustring& hostname); virtual ~SessionView(); const InfSession* get_session() const { return m_session; } InfSession* get_session() { return m_session; } const Glib::ustring& get_title() const { return m_title; } const Glib::ustring& get_path() const { return m_path; } const Glib::ustring& get_hostname() const { return m_hostname; } void set_info(const Glib::ustring& info, bool closable); void unset_info(); virtual InfUser* get_active_user() const; SignalActiveUserChanged signal_active_user_changed() const { return m_signal_active_user_changed; } protected: void active_user_changed(InfUser* new_user); InfSession* m_session; const Glib::ustring m_title; const Glib::ustring m_path; const Glib::ustring m_hostname; Gtk::Frame m_info_frame; Gtk::Grid m_info_grid; CloseButton m_info_close_button; Gtk::Label m_info_label; private: SignalActiveUserChanged m_signal_active_user_changed; }; } #endif // _GOBBY_SESSIONVIEW_HPP_ code/core/statusbar.cpp000066400000000000000000000263441304552132100154410ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "core/statusbar.hpp" #include "util/i18n.hpp" #include #include #include namespace { const gchar* message_type_to_icon_name(Gobby::StatusBar::MessageType type) { switch(type) { case Gobby::StatusBar::INFO: return "dialog-information"; case Gobby::StatusBar::ERROR: return "dialog-error"; default: g_assert_not_reached(); return NULL; } } void dispose_dialog(Gtk::MessageDialog* dialog) { delete dialog; } } class Gobby::StatusBar::Message { public: Message(Gtk::Widget* widget, const Glib::ustring& simple, const Glib::ustring& detail, sigc::connection timeout_conn = sigc::connection()): m_widget(widget), m_timeout_conn(timeout_conn), m_simple_desc(simple), m_detail_desc(detail) { } ~Message() { m_timeout_conn.disconnect(); } void show_dialog() const { Gtk::MessageDialog* dialog = new Gtk::MessageDialog( m_simple_desc, false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_NONE, false); Gtk::Window* parent = NULL; Gtk::Widget* toplevel_widget = m_widget->get_toplevel(); if(gtk_widget_is_toplevel(toplevel_widget->gobj())) parent = dynamic_cast(toplevel_widget); g_assert(parent != NULL); dialog->set_transient_for(*parent); dialog->add_button(_("_Close"), Gtk::RESPONSE_CLOSE); dialog->set_secondary_text(m_detail_desc, true); dialog->signal_response().connect( sigc::hide( sigc::bind( sigc::ptr_fun(dispose_dialog), dialog))); dialog->show(); } bool is_error() { return !m_detail_desc.empty(); } const Glib::ustring& get_simple_text() const { return m_simple_desc; } const Glib::ustring& get_detail_text() const { return m_detail_desc; } Gtk::Widget* widget() const { return m_widget; } protected: Gtk::Widget* m_widget; sigc::connection m_timeout_conn; Glib::ustring m_simple_desc; Glib::ustring m_detail_desc; }; Gobby::StatusBar::StatusBar(const Folder& folder, const Preferences& preferences): m_folder(folder), m_preferences(preferences), m_visible_messages(0), m_current_view(NULL) { set_column_spacing(2); m_lbl_position.set_halign(Gtk::ALIGN_END); m_lbl_position.set_hexpand(true); #if GTK_CHECK_VERSION(3,12,0) gtk_widget_set_margin_end(GTK_WIDGET(m_lbl_position.gobj()), 6); #else gtk_widget_set_margin_right(GTK_WIDGET(m_lbl_position.gobj()), 6); #endif m_lbl_position.show(); attach(m_lbl_position, 0, 0, 1, 1); m_folder.signal_document_removed().connect( sigc::mem_fun(*this, &StatusBar::on_document_removed)); m_folder.signal_document_changed().connect( sigc::mem_fun(*this, &StatusBar::on_document_changed)); m_preferences.appearance.show_statusbar.signal_changed().connect( sigc::mem_fun(*this, &StatusBar::on_view_changed)); // Initial update on_document_changed(m_folder.get_current_document()); on_view_changed(); } Gobby::StatusBar::~StatusBar() { on_document_changed(NULL); } Gobby::StatusBar::MessageHandle Gobby::StatusBar::add_message(Gobby::StatusBar::MessageType type, const Glib::ustring& message, const Glib::ustring& dialog_message, unsigned int timeout) { if(m_visible_messages >= 12) { for(MessageHandle iter = m_list.begin(); iter != m_list.end(); ++iter) { if(*iter) { if((*iter)->is_error()) remove_message(iter); else // only hide message because whoever // installed it is expecting to be // able to call remove_message on it hide_message(iter); break; } } } Gtk::Grid* grid = Gtk::manage(new Gtk::Grid()); grid->set_column_spacing(6); #if GTK_CHECK_VERSION(3,12,0) gtk_widget_set_margin_start(GTK_WIDGET(grid->gobj()), 2); gtk_widget_set_margin_end(GTK_WIDGET(grid->gobj()), 2); #else gtk_widget_set_margin_left(GTK_WIDGET(grid->gobj()), 2); gtk_widget_set_margin_right(GTK_WIDGET(grid->gobj()), 2); #endif Gtk::Image* image = Gtk::manage(new Gtk::Image); image->set_from_icon_name(message_type_to_icon_name(type), Gtk::ICON_SIZE_MENU); grid->attach(*image, 0, 0, 1, 1); image->show(); Gtk::Label* label = Gtk::manage( new Gtk::Label(message, Gtk::ALIGN_START)); label->set_ellipsize(Pango::ELLIPSIZE_END); // If we set halign instead, the label will not behave correctly // when ellipsized, because then it has always all space around it // allocated, and the alignment "jumps" around whin resizing the // window due to new characters appearing or disappearing as a result // of the ellipsization. #if GTK_CHECK_VERSION(3, 16, 0) gtk_label_set_xalign(label->gobj(), 0.0); #else label->set_alignment(0.0, 0.0); #endif label->show(); grid->attach(*label, 1, 0, 1, 1); Gtk::Frame* frame = Gtk::manage(new Gtk::Frame); m_list.push_back(0); Gobby::StatusBar::MessageHandle iter(--m_list.end()); sigc::connection timeout_conn; if(timeout) { timeout_conn = Glib::signal_timeout().connect_seconds( sigc::bind( sigc::bind_return( sigc::mem_fun( *this, &StatusBar::remove_message), false), iter), timeout); } *iter = new Message(frame, message, dialog_message, timeout_conn); ++m_visible_messages; if(dialog_message.empty()) { frame->add(*grid); } else { Gtk::EventBox *eventbox = Gtk::manage(new Gtk::EventBox); frame->add(*eventbox); eventbox->add(*grid); eventbox->signal_button_press_event().connect( sigc::bind_return(sigc::bind( sigc::mem_fun( *this, &StatusBar::on_message_clicked), iter), false)); eventbox->show(); } grid->show(); // Insert at front gtk_grid_attach_next_to( gobj(), GTK_WIDGET(frame->gobj()), NULL, GTK_POS_LEFT, 1, 1); frame->set_halign(Gtk::ALIGN_START); frame->set_hexpand(false); frame->set_shadow_type(Gtk::SHADOW_NONE); frame->show(); return iter; } Gobby::StatusBar::MessageHandle Gobby::StatusBar::add_info_message(const Glib::ustring& message, unsigned int timeout) { MessageHandle handle = Gobby::StatusBar::add_message(INFO, message, "", timeout); // Caller is not allowed to hold on to handles to messages that we are // going to delete anyway. if(timeout) return invalid_handle(); else return handle; } void Gobby::StatusBar::add_error_message(const Glib::ustring& brief_desc, const Glib::ustring& detailed_desc, unsigned int timeout) { MessageHandle next; for(MessageHandle iter = m_list.begin(); iter != m_list.end(); iter = next) { next = iter; ++next; if(*iter && (*iter)->is_error()) { if( (*iter)->get_simple_text() == brief_desc) { remove_message(iter); } } } Gobby::StatusBar::add_message(ERROR, brief_desc, detailed_desc, timeout); } void Gobby::StatusBar::remove_message(const MessageHandle& handle) { hide_message(handle); m_list.erase(handle); } void Gobby::StatusBar::hide_message(const MessageHandle& handle) { if(*handle != 0) { g_assert(m_visible_messages > 0); --m_visible_messages; remove(*(*handle)->widget()); delete *handle; *handle = 0; } } Gobby::StatusBar::MessageHandle Gobby::StatusBar::invalid_handle() { return m_list.end(); } void Gobby::StatusBar::on_message_clicked(GdkEventButton* button, const MessageHandle& handle) { if(button->button == 1) (*handle)->show_dialog(); remove_message(handle); } void Gobby::StatusBar::on_document_removed(SessionView& view) { if(m_current_view == &view) { GtkTextBuffer* buffer = GTK_TEXT_BUFFER( m_current_view->get_text_buffer()); g_signal_handler_disconnect(buffer, m_mark_set_handler); g_signal_handler_disconnect(buffer, m_changed_handler); g_signal_handler_disconnect(m_current_view->get_text_view(), m_toverwrite_handler); m_current_view = NULL; } } void Gobby::StatusBar::on_document_changed(SessionView* view) { if(m_current_view) { GtkTextBuffer* buffer = GTK_TEXT_BUFFER( m_current_view->get_text_buffer()); g_signal_handler_disconnect(buffer, m_mark_set_handler); g_signal_handler_disconnect(buffer, m_changed_handler); g_signal_handler_disconnect(m_current_view->get_text_view(), m_toverwrite_handler); } m_current_view = dynamic_cast(view); if(m_current_view) { GtkTextBuffer* buffer = GTK_TEXT_BUFFER( m_current_view->get_text_buffer()); m_mark_set_handler = g_signal_connect_after( G_OBJECT(buffer), "mark-set", G_CALLBACK(on_mark_set_static), this); m_changed_handler = g_signal_connect_after( G_OBJECT(buffer), "changed", G_CALLBACK(on_changed_static), this); m_toverwrite_handler = g_signal_connect_after( G_OBJECT(m_current_view->get_text_view()), "notify::overwrite", G_CALLBACK(on_toggled_overwrite_static), this); } // Initial update update_pos_display(); } void Gobby::StatusBar::on_view_changed() { if(m_preferences.appearance.show_statusbar) show(); else hide(); } void Gobby::StatusBar::on_mark_set(GtkTextMark* mark) { GtkTextBuffer* buffer = GTK_TEXT_BUFFER( m_current_view->get_text_buffer()); if(mark == gtk_text_buffer_get_insert(buffer)) update_pos_display(); } void Gobby::StatusBar::on_toggled_overwrite() { update_pos_display(); } void Gobby::StatusBar::on_changed() { update_pos_display(); } void Gobby::StatusBar::update_pos_display() { if(m_current_view != NULL) { GtkTextBuffer* buffer = GTK_TEXT_BUFFER( m_current_view->get_text_buffer()); GtkTextIter iter; // TODO: Use TextSessionView::get_cursor_position()? gtk_text_buffer_get_iter_at_mark( buffer, &iter, gtk_text_buffer_get_insert(buffer)); gint offset = gtk_text_iter_get_line_offset(&iter); unsigned int column = 0; const unsigned int tab_width = m_preferences.editor.tab_width; for(gtk_text_iter_set_line_offset(&iter, 0); gtk_text_iter_get_line_offset(&iter) < offset; gtk_text_iter_forward_char(&iter)) { if(gtk_text_iter_get_char(&iter) == '\t') column += (tab_width - column % tab_width); else ++ column; } // TODO: We might want to have a separate widget for the // OVR/INS display. m_lbl_position.set_text( Glib::ustring::compose( _("Ln %1, Col %2\t%3"), gtk_text_iter_get_line(&iter) + 1, column + 1, gtk_text_view_get_overwrite(GTK_TEXT_VIEW(m_current_view->get_text_view())) ? _("OVR") : _("INS") ) ); } else { m_lbl_position.set_text(""); } } code/core/statusbar.hpp000066400000000000000000000067121304552132100154430ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_STATUSBAR_HPP_ #define _GOBBY_STATUSBAR_HPP_ #include "folder.hpp" #include #include #include #include /* ARGH!!! */ #ifdef G_OS_WIN32 # ifdef ERROR # undef ERROR # endif #endif namespace Gobby { class StatusBar: public Gtk::Grid { protected: class Message; typedef std::list MessageList; public: enum MessageType { INFO, ERROR }; typedef MessageList::iterator MessageHandle; StatusBar(const Folder& folder, const Preferences& preferences); ~StatusBar(); MessageHandle add_info_message(const Glib::ustring& message, unsigned int timeout = 0); // This does not return a message handle because users dispose of // messages by clicking on them. void add_error_message(const Glib::ustring& brief_desc, const Glib::ustring& detailed_desc, unsigned int timeout = 0); void remove_message(const MessageHandle& handle); void hide_message(const MessageHandle& handle); MessageHandle invalid_handle(); protected: MessageHandle add_message(MessageType type, const Glib::ustring& message, const Glib::ustring& dialog_message, unsigned int timeout = 0); static void on_mark_set_static(GtkTextBuffer* buffer, GtkTextIter* location, GtkTextMark* mark, gpointer user_data) { static_cast(user_data)->on_mark_set(mark); } static void on_changed_static(GtkTextBuffer* buffer, gpointer user_data) { static_cast(user_data)->on_changed(); } static void on_toggled_overwrite_static(GtkTextView* buffer, GParamSpec* pspec, gpointer user_data) { static_cast(user_data)->on_toggled_overwrite(); } void on_message_clicked(GdkEventButton* button, const MessageHandle& handle); void on_document_removed(SessionView& view); void on_document_changed(SessionView* view); void on_view_changed(); void on_mark_set(GtkTextMark* mark); void on_toggled_overwrite(); void on_changed(); void update_pos_display(); const Folder& m_folder; const Preferences& m_preferences; MessageList m_list; unsigned int m_visible_messages; Gtk::Label m_lbl_position; TextSessionView* m_current_view; gulong m_mark_set_handler; gulong m_changed_handler; gulong m_toverwrite_handler; }; } #endif // _GOBBY_STATUSBAR_HPP_ code/core/tablabel.cpp000066400000000000000000000104241304552132100151670ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "core/tablabel.hpp" #include "core/folder.hpp" Gobby::TabLabel::TabLabel(Folder& folder, SessionView& view, const Glib::ustring& active_icon_name): m_folder(folder), m_view(view), m_title(view.get_title()), m_changed(false), m_active_icon_name(active_icon_name) { set_column_spacing(6); m_title.set_halign(Gtk::ALIGN_START); update_icon(); update_color(); m_icon.show(); m_title.show(); m_button.set_halign(Gtk::ALIGN_END); m_button.show(); view.signal_active_user_changed().connect( sigc::mem_fun(*this, &TabLabel::on_active_user_changed)); m_notify_status_handle = g_signal_connect( G_OBJECT(view.get_session()), "notify::status", G_CALLBACK(on_notify_status_static), this); m_notify_subscription_group_handle = g_signal_connect( G_OBJECT(view.get_session()), "notify::subscription-group", G_CALLBACK(on_notify_subscription_group_static), this); m_folder.signal_document_changed().connect( sigc::mem_fun(*this, &TabLabel::on_folder_document_changed)); attach(m_icon, 0, 0, 1, 1); attach(m_title, 1, 0, 1, 1); attach(m_button, 2, 0, 1, 1); } Gobby::TabLabel::~TabLabel() { g_signal_handler_disconnect(m_view.get_session(), m_notify_status_handle); g_signal_handler_disconnect(m_view.get_session(), m_notify_subscription_group_handle); } void Gobby::TabLabel::on_folder_document_changed(SessionView* view) { if(view == &m_view) on_activate(); } void Gobby::TabLabel::on_active_user_changed(InfUser* user) { update_icon(); } void Gobby::TabLabel::on_notify_status() { update_icon(); update_color(); } void Gobby::TabLabel::on_notify_subscription_group() { update_icon(); update_color(); } void Gobby::TabLabel::on_activate() { m_changed = false; update_color(); } void Gobby::TabLabel::set_changed() { if(m_folder.get_current_document() != &m_view) { m_changed = true; update_color(); } } void Gobby::TabLabel::update_icon() { InfSession* session = INF_SESSION(m_view.get_session()); if(inf_session_get_subscription_group(session) == NULL) { m_icon.set_from_icon_name("network-offline", Gtk::ICON_SIZE_MENU); } else { switch(inf_session_get_status(session)) { case INF_SESSION_PRESYNC: case INF_SESSION_SYNCHRONIZING: // TODO: Switch to process-working, if/when m_icon can // show animations. m_icon.set_from_icon_name("system-run", Gtk::ICON_SIZE_MENU); break; case INF_SESSION_RUNNING: if(m_view.get_active_user() != NULL) { m_icon.set_from_icon_name(m_active_icon_name, Gtk::ICON_SIZE_MENU); } else { m_icon.set_from_icon_name("text-x-generic", Gtk::ICON_SIZE_MENU); } break; case INF_SESSION_CLOSED: m_icon.set_from_icon_name("network-offline", Gtk::ICON_SIZE_MENU); break; } } } void Gobby::TabLabel::update_color() { InfSession* session = INF_SESSION(m_view.get_session()); if(m_changed) { // Document has changed: awareness -> red m_title.override_color(Gdk::RGBA("#c00000")); } else if(inf_session_get_subscription_group(session) == NULL || inf_session_get_status(session) != INF_SESSION_RUNNING) { // Document disconnected or not yet running // (most probably synchronizing): not (yet) available -> grey m_title.override_color(Gdk::RGBA("#606060")); } else { // Otherwise default m_title.unset_color(); } } code/core/tablabel.hpp000066400000000000000000000050021304552132100151700ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_TABLABEL_HPP_ #define _GOBBY_TABLABEL_HPP_ #include "core/sessionview.hpp" #include #include namespace Gobby { class Folder; class TabLabel: public Gtk::Grid { public: typedef Glib::SignalProxy0 SignalCloseRequest; TabLabel(Folder& folder, SessionView& view, const Glib::ustring& active_icon_name); virtual ~TabLabel(); SignalCloseRequest signal_close_request() { return m_button.signal_clicked(); } protected: static void on_notify_status_static(GObject* object, GParamSpec* pspec, gpointer user_data) { static_cast(user_data)->on_notify_status(); } static void on_notify_subscription_group_static(GObject* object, GParamSpec* pspec, gpointer user_data) { static_cast(user_data)-> on_notify_subscription_group(); } void on_folder_document_changed(SessionView* view); // Can be overriden by derived classes: virtual void on_active_user_changed(InfUser* user); virtual void on_notify_status(); virtual void on_notify_subscription_group(); virtual void on_activate(); // To be called by derived classes: void set_changed(); Folder& m_folder; SessionView& m_view; Gtk::Image m_icon; Gtk::Label m_title; CloseButton m_button; bool m_changed; private: void update_icon(); void update_color(); const Glib::ustring m_active_icon_name; // Whether the document was changed since it has been active. gulong m_notify_status_handle; gulong m_notify_subscription_group_handle; }; } #endif // _GOBBY_TABLABEL_HPP_ code/core/textsessionuserview.cpp000066400000000000000000000041701304552132100176040ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "core/textsessionuserview.hpp" Gobby::TextSessionUserView:: TextSessionUserView(TextSessionView& view, bool show_disconnected, Preferences::Option& opt_view, Preferences::Option& w): SessionUserView(view, show_disconnected, opt_view, w) { m_userlist.signal_user_activated().connect( sigc::mem_fun( *this, &TextSessionUserView::on_user_activated)); } void Gobby::TextSessionUserView::on_user_activated(InfUser* user) { g_assert(INF_TEXT_IS_USER(user)); InfTextUser* text_user = INF_TEXT_USER(user); // TODO: Instead, move this code to // TextSessionView::scroll_to_cursor_position which should take // an additional InfTextUser* argument GtkSourceBuffer* buffer = get_session_view().get_text_buffer(); GtkSourceView* view = get_session_view().get_text_view(); // Use a mark to make sure we scroll it onscreen GtkTextIter iter; gtk_text_buffer_get_iter_at_offset( GTK_TEXT_BUFFER(buffer), &iter, inf_text_user_get_caret_position(text_user)); GtkTextMark* mark = gtk_text_buffer_create_mark( GTK_TEXT_BUFFER(buffer), NULL, &iter, FALSE); gtk_text_view_scroll_to_mark( GTK_TEXT_VIEW(view), mark, 0.0, TRUE, 0.5, 0.5); gtk_text_buffer_delete_mark(GTK_TEXT_BUFFER(buffer), mark); } code/core/textsessionuserview.hpp000066400000000000000000000034201304552132100176060ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_TEXTSESSIONUSERVIEW_HPP_ #define _GOBBY_TEXTSESSIONUSERVIEW_HPP_ #include "core/sessionuserview.hpp" #include "core/textsessionview.hpp" #include "core/preferences.hpp" #include "core/userlist.hpp" // Allows a user in the user list to be double-clicked at, scrolling // the text view to that user's cursor. namespace Gobby { class TextSessionUserView: public SessionUserView { public: TextSessionUserView(TextSessionView& view, bool show_disconnected, Preferences::Option& userlist_view, Preferences::Option& w); TextSessionView& get_session_view() { return static_cast( SessionUserView::get_session_view()); } const TextSessionView& get_session_view() const { return static_cast( SessionUserView::get_session_view()); } protected: void on_user_activated(InfUser* user); }; } #endif // _GOBBY_TEXTSESSIONUSERVIEW_HPP_ code/core/textsessionview.cpp000066400000000000000000000475531304552132100167210ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "core/gobject/gobby-undo-manager.h" #include "core/textsessionview.hpp" #include "util/i18n.hpp" #include #include #include #include #include #include #include #include #include // TODO: Put all the preferences handling into an extra class namespace { GtkWrapMode wrap_mode_from_preferences(const Gobby::Preferences& pref) { return static_cast( static_cast(pref.view.wrap_mode)); } bool glob_matches(const gchar* const* globs, const std::string& str) { if(globs) { for(const gchar* const* glob = globs; *glob != NULL; ++ glob) { Glib::PatternSpec spec(*glob); if(spec.match(str)) return true; } } return false; } bool language_matches_title(GtkSourceLanguage* language, const gchar* title) { bool result = false; gchar** globs = gtk_source_language_get_globs(language); if(glob_matches(globs, title)) result = true; g_strfreev(globs); return result; } GtkSourceLanguage* get_language_for_title(GtkSourceLanguageManager* manager, const gchar* title) { const gchar* const* ids = gtk_source_language_manager_get_language_ids(manager); if(ids) { for(const gchar* const* id = ids; *id != NULL; ++ id) { GtkSourceLanguage* l; l = gtk_source_language_manager_get_language( manager, *id); if(l) if(language_matches_title(l, title)) return l; } } return NULL; } bool tags_priority_idle_func(Gobby::TextSessionView& view) { InfTextGtkBuffer* buffer = INF_TEXT_GTK_BUFFER( inf_session_get_buffer( INF_SESSION(view.get_session()))); inf_text_gtk_buffer_ensure_author_tags_priority(buffer); // I don't know why it does not redraw automatically, perhaps // this is a bug. gtk_widget_queue_draw(GTK_WIDGET(view.get_text_view())); return false; } void on_tag_added(GtkTextTagTable* table, GtkTextTag* tag, gpointer user_data) { // We do the actual reordering in an idle handler because // the priority of the tag might not yet be set to its final // value. Glib::signal_idle().connect( sigc::bind( sigc::ptr_fun(tags_priority_idle_func), sigc::ref( *static_cast( user_data)))); } } Gobby::TextSessionView::TextSessionView(InfTextSession* session, const Glib::ustring& title, const Glib::ustring& path, const Glib::ustring& hostname, const std::string& info_storage_key, Preferences& preferences, GtkSourceLanguageManager* manager): SessionView(INF_SESSION(session), title, path, hostname), m_info_storage_key(info_storage_key), m_preferences(preferences), m_view(GTK_SOURCE_VIEW(gtk_source_view_new())) { InfBuffer* buffer = inf_session_get_buffer(INF_SESSION(session)); InfUserTable* user_table = inf_session_get_user_table(INF_SESSION(session)); m_buffer = GTK_SOURCE_BUFFER(inf_text_gtk_buffer_get_text_buffer( INF_TEXT_GTK_BUFFER(buffer))); m_infview = inf_text_gtk_view_new( inf_adopted_session_get_io(INF_ADOPTED_SESSION(session)), GTK_TEXT_VIEW(m_view), user_table); g_signal_connect_after( G_OBJECT(m_view), "style-updated", G_CALLBACK(on_view_style_updated_static), this); // This is a hack to make sure that the author tags in the textview // have lowest priority of all tags, especially lower than // GtkSourceView's FIXME tags. We do this every time a new tag is // added to the tag table since GtkSourceView seems to create tags // that it needs on the fly. GtkTextTagTable* table = gtk_text_buffer_get_tag_table( GTK_TEXT_BUFFER(m_buffer)); g_signal_connect(G_OBJECT(table), "tag-added", G_CALLBACK(on_tag_added), this); gtk_widget_set_has_tooltip(GTK_WIDGET(m_view), TRUE); g_signal_connect(m_view, "query-tooltip", G_CALLBACK(on_query_tooltip_static), this); gtk_source_buffer_set_style_scheme( m_buffer, gtk_source_style_scheme_manager_get_scheme( gtk_source_style_scheme_manager_get_default(), static_cast( preferences.appearance.scheme_id).c_str())); gtk_text_view_set_buffer(GTK_TEXT_VIEW(m_view), GTK_TEXT_BUFFER(m_buffer)); gtk_text_view_set_editable(GTK_TEXT_VIEW(m_view), FALSE); set_language(get_language_for_title(manager, title.c_str())); m_preferences.user.hue.signal_changed().connect( sigc::mem_fun( *this, &TextSessionView::on_user_color_changed)); m_preferences.user.alpha.signal_changed().connect( sigc::mem_fun( *this, &TextSessionView::on_alpha_changed)); m_preferences.user.show_remote_cursors.signal_changed().connect( sigc::mem_fun( *this, &TextSessionView::on_show_remote_cursors_changed)); m_preferences.user.show_remote_selections.signal_changed().connect( sigc::mem_fun( *this, &TextSessionView::on_show_remote_selections_changed)); m_preferences.user.show_remote_current_lines.signal_changed().connect( sigc::mem_fun( *this, &TextSessionView::on_show_remote_current_lines_changed)); m_preferences.user.show_remote_cursor_positions.signal_changed().connect( sigc::mem_fun( *this, &TextSessionView::on_show_remote_cursor_positions_changed)); m_preferences.editor.tab_width.signal_changed().connect( sigc::mem_fun( *this, &TextSessionView::on_tab_width_changed)); m_preferences.editor.tab_spaces.signal_changed().connect( sigc::mem_fun( *this, &TextSessionView::on_tab_spaces_changed)); m_preferences.editor.indentation_auto.signal_changed().connect( sigc::mem_fun( *this, &TextSessionView::on_auto_indent_changed)); m_preferences.editor.homeend_smart.signal_changed().connect( sigc::mem_fun( *this, &TextSessionView::on_homeend_smart_changed)); m_preferences.view.wrap_mode.signal_changed().connect( sigc::mem_fun( *this, &TextSessionView::on_wrap_mode_changed)); m_preferences.view.linenum_display.signal_changed().connect( sigc::mem_fun( *this, &TextSessionView::on_linenum_display_changed)); m_preferences.view.curline_highlight.signal_changed().connect( sigc::mem_fun( *this, &TextSessionView::on_curline_highlight_changed)); m_preferences.view.margin_display.signal_changed().connect( sigc::mem_fun( *this, &TextSessionView::on_margin_display_changed)); m_preferences.view.margin_pos.signal_changed().connect( sigc::mem_fun( *this, &TextSessionView::on_margin_pos_changed)); m_preferences.view.bracket_highlight.signal_changed().connect( sigc::mem_fun( *this, &TextSessionView::on_bracket_highlight_changed)); m_preferences.view.whitespace_display.signal_changed().connect( sigc::mem_fun( *this, &TextSessionView::on_whitespace_display_changed)); m_preferences.appearance.font.signal_changed().connect( sigc::mem_fun(*this, &TextSessionView::on_font_changed)); m_preferences.appearance.scheme_id.signal_changed().connect( sigc::mem_fun(*this, &TextSessionView::on_scheme_changed)); inf_text_gtk_view_set_show_remote_cursors( m_infview, m_preferences.user.show_remote_cursors ); inf_text_gtk_view_set_show_remote_selections( m_infview, m_preferences.user.show_remote_selections ); inf_text_gtk_view_set_show_remote_current_lines( m_infview, m_preferences.user.show_remote_current_lines ); inf_text_gtk_buffer_set_fade( INF_TEXT_GTK_BUFFER(buffer), m_preferences.user.alpha); gtk_source_view_set_tab_width(m_view, m_preferences.editor.tab_width); gtk_source_view_set_insert_spaces_instead_of_tabs( m_view, m_preferences.editor.tab_spaces); gtk_source_view_set_auto_indent( m_view, m_preferences.editor.indentation_auto); gtk_source_view_set_smart_home_end( m_view, m_preferences.editor.homeend_smart ? GTK_SOURCE_SMART_HOME_END_AFTER : GTK_SOURCE_SMART_HOME_END_DISABLED); gtk_text_view_set_wrap_mode( GTK_TEXT_VIEW(m_view), wrap_mode_from_preferences(m_preferences)); gtk_source_view_set_show_line_numbers( m_view, m_preferences.view.linenum_display); gtk_source_view_set_highlight_current_line( m_view, m_preferences.view.curline_highlight); gtk_source_view_set_show_right_margin( m_view, m_preferences.view.margin_display); gtk_source_view_set_right_margin_position( m_view, m_preferences.view.margin_pos); gtk_source_buffer_set_highlight_matching_brackets( m_buffer, m_preferences.view.bracket_highlight); gtk_source_view_set_draw_spaces( m_view, m_preferences.view.whitespace_display); gtk_widget_show(GTK_WIDGET(m_view)); Gtk::ScrolledWindow* scroll = Gtk::manage(new Gtk::ScrolledWindow); scroll->set_shadow_type(Gtk::SHADOW_IN); scroll->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); scroll->set_hexpand(true); scroll->set_vexpand(true); gtk_container_add(GTK_CONTAINER(scroll->gobj()), GTK_WIDGET(m_view)); scroll->show(); m_infviewport = inf_text_gtk_viewport_new(scroll->gobj(), user_table); inf_text_gtk_viewport_set_show_user_markers( m_infviewport, m_preferences.user.show_remote_cursor_positions ); attach_next_to(*scroll, m_info_frame, Gtk::POS_BOTTOM, 1, 1); // Set initial font on_font_changed(); } Gobby::TextSessionView::~TextSessionView() { g_object_unref(m_infview); g_object_unref(m_infviewport); } void Gobby::TextSessionView::get_cursor_position(unsigned int& row, unsigned int& col) const { GtkTextMark* insert_mark = gtk_text_buffer_get_insert(GTK_TEXT_BUFFER(m_buffer)); GtkTextIter iter; gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(m_buffer), &iter, insert_mark); row = gtk_text_iter_get_line(&iter); col = 0; int chars = gtk_text_iter_get_line_offset(&iter); unsigned int tabs = m_preferences.editor.tab_width; // Tab characters expand to more than one column for(gtk_text_iter_set_line_offset(&iter, 0); gtk_text_iter_get_line_offset(&iter) < chars; gtk_text_iter_forward_char(&iter)) { unsigned int width = 1; if(gtk_text_iter_get_char(&iter) == '\t') { unsigned int offset = gtk_text_iter_get_line_offset(&iter); width = (tabs - offset % tabs) % tabs; if(width == 0) width = tabs; } col += width; } } void Gobby::TextSessionView::set_selection(const GtkTextIter* begin, const GtkTextIter* end) { gtk_text_buffer_select_range( gtk_text_view_get_buffer(GTK_TEXT_VIEW(m_view)), begin, end); scroll_to_cursor_position(0.1); } Glib::ustring Gobby::TextSessionView::get_selected_text() const { GtkTextIter start, end; gtk_text_buffer_get_selection_bounds( gtk_text_view_get_buffer(GTK_TEXT_VIEW(m_view)), &start, &end); Gtk::TextIter start_cpp(&start), end_cpp(&end); return start_cpp.get_slice(end_cpp); } void Gobby::TextSessionView::scroll_to_cursor_position(double within_margin) { gtk_text_view_scroll_to_mark( GTK_TEXT_VIEW(m_view), gtk_text_buffer_get_insert(gtk_text_view_get_buffer( GTK_TEXT_VIEW(m_view))), within_margin, FALSE, 0.0, 0.0); } InfUser* Gobby::TextSessionView::get_active_user() const { InfTextGtkBuffer* buffer = INF_TEXT_GTK_BUFFER( inf_session_get_buffer(INF_SESSION(m_session))); return INF_USER(inf_text_gtk_buffer_get_active_user(buffer)); } void Gobby::TextSessionView::set_active_user(InfTextUser* user) { g_assert( user == NULL || inf_user_table_lookup_user_by_id( inf_session_get_user_table(INF_SESSION(m_session)), inf_user_get_id(INF_USER(user))) == INF_USER(user)); inf_text_gtk_buffer_set_active_user( INF_TEXT_GTK_BUFFER( inf_session_get_buffer(INF_SESSION(m_session))), user); inf_text_gtk_view_set_active_user(m_infview, user); inf_text_gtk_viewport_set_active_user(m_infviewport, user); // TODO: Make sure the active user has the color specified in the // preferences, and set color if not. if(user != NULL) gtk_text_view_set_editable(GTK_TEXT_VIEW(m_view), TRUE); else gtk_text_view_set_editable(GTK_TEXT_VIEW(m_view), FALSE); active_user_changed(INF_USER(user)); if(user != NULL) { m_undo_grouping.reset( new TextUndoGrouping( inf_adopted_session_get_algorithm( INF_ADOPTED_SESSION(m_session)), user, GTK_TEXT_BUFFER(m_buffer))); GobbyUndoManager* undo_manager = gobby_undo_manager_new( INF_TEXT_SESSION(m_session), m_undo_grouping->get_inf_grouping()); gtk_source_buffer_set_undo_manager( m_buffer, GTK_SOURCE_UNDO_MANAGER(undo_manager)); g_object_unref(undo_manager); } else { m_undo_grouping.reset(NULL); gtk_source_buffer_set_undo_manager(m_buffer, NULL); } } GtkSourceLanguage* Gobby::TextSessionView::get_language() const { return gtk_source_buffer_get_language(m_buffer); } void Gobby::TextSessionView::set_language(GtkSourceLanguage* language) { gtk_source_buffer_set_language(m_buffer, language); m_signal_language_changed.emit(language); } void Gobby::TextSessionView::on_user_color_changed() { InfTextUser* user = INF_TEXT_USER(get_active_user()); if(user) { inf_text_session_set_user_color(get_session(), user, m_preferences.user.hue); } } void Gobby::TextSessionView::on_alpha_changed() { InfTextGtkBuffer* buffer = INF_TEXT_GTK_BUFFER( inf_session_get_buffer(INF_SESSION(m_session))); inf_text_gtk_buffer_set_fade( buffer, m_preferences.user.alpha); } void Gobby::TextSessionView::on_show_remote_cursors_changed() { inf_text_gtk_view_set_show_remote_cursors( m_infview, m_preferences.user.show_remote_cursors ); } void Gobby::TextSessionView::on_show_remote_selections_changed() { inf_text_gtk_view_set_show_remote_selections( m_infview, m_preferences.user.show_remote_selections ); } void Gobby::TextSessionView::on_show_remote_current_lines_changed() { inf_text_gtk_view_set_show_remote_current_lines( m_infview, m_preferences.user.show_remote_current_lines ); } void Gobby::TextSessionView::on_show_remote_cursor_positions_changed() { inf_text_gtk_viewport_set_show_user_markers( m_infviewport, m_preferences.user.show_remote_cursor_positions ); } void Gobby::TextSessionView::on_tab_width_changed() { gtk_source_view_set_tab_width(m_view, m_preferences.editor.tab_width); } void Gobby::TextSessionView::on_tab_spaces_changed() { gtk_source_view_set_insert_spaces_instead_of_tabs( m_view, m_preferences.editor.tab_spaces); } void Gobby::TextSessionView::on_auto_indent_changed() { gtk_source_view_set_auto_indent( m_view, m_preferences.editor.indentation_auto); } void Gobby::TextSessionView::on_homeend_smart_changed() { gtk_source_view_set_smart_home_end( m_view, m_preferences.editor.homeend_smart ? GTK_SOURCE_SMART_HOME_END_AFTER : GTK_SOURCE_SMART_HOME_END_DISABLED); } void Gobby::TextSessionView::on_wrap_mode_changed() { gtk_text_view_set_wrap_mode( GTK_TEXT_VIEW(m_view), wrap_mode_from_preferences(m_preferences)); } void Gobby::TextSessionView::on_linenum_display_changed() { gtk_source_view_set_show_line_numbers( m_view, m_preferences.view.linenum_display); } void Gobby::TextSessionView::on_curline_highlight_changed() { gtk_source_view_set_highlight_current_line( m_view, m_preferences.view.curline_highlight); } void Gobby::TextSessionView::on_margin_display_changed() { gtk_source_view_set_show_right_margin( m_view, m_preferences.view.margin_display); } void Gobby::TextSessionView::on_margin_pos_changed() { gtk_source_view_set_right_margin_position( m_view, m_preferences.view.margin_pos); } void Gobby::TextSessionView::on_bracket_highlight_changed() { gtk_source_buffer_set_highlight_matching_brackets( m_buffer, m_preferences.view.bracket_highlight); } void Gobby::TextSessionView::on_whitespace_display_changed() { gtk_source_view_set_draw_spaces( m_view, m_preferences.view.whitespace_display); } void Gobby::TextSessionView::on_font_changed() { const Pango::FontDescription& desc = m_preferences.appearance.font; if(!m_font_provider) { m_font_provider = Gtk::CssProvider::create(); get_style_context()->add_provider( m_font_provider, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); } const Glib::ustring font_style = Glib::ustring::compose( "* {\n" " font-size: %1pt;\n" " font-family: \"%2\";\n" " font-weight: %3;\n" "}", desc.get_size() / PANGO_SCALE, desc.get_family(), desc.get_weight()); try { m_font_provider->load_from_data(font_style); } catch (const Glib::Error& ex) { g_warning("Failed to apply font settings: %s", ex.what().c_str()); } } void Gobby::TextSessionView::on_scheme_changed() { gtk_source_buffer_set_style_scheme( m_buffer, gtk_source_style_scheme_manager_get_scheme( gtk_source_style_scheme_manager_get_default(), static_cast( m_preferences.appearance.scheme_id).c_str())); } bool Gobby::TextSessionView:: on_query_tooltip(int x, int y, bool keyboard_mode, const Glib::RefPtr& tooltip) { if(keyboard_mode) return false; int buffer_x, buffer_y; gtk_text_view_window_to_buffer_coords( GTK_TEXT_VIEW(m_view), GTK_TEXT_WINDOW_WIDGET, x, y, &buffer_x, &buffer_y); // Pointer is in line number display if(buffer_x < 0) return false; // Get the character at the mouse position GtkTextIter iter; int trailing; gtk_text_view_get_iter_at_position( GTK_TEXT_VIEW(m_view), &iter, &trailing, buffer_x, buffer_y); // Don't show a tooltip if the character is a newline character */ //if(gtk_text_iter_is_end(&iter)) return false; if(gtk_text_iter_ends_line(&iter)) return false; // Don't show a tooltip if we are past the end of the line GdkRectangle newline_location; GtkTextIter line_end = iter; gtk_text_iter_forward_to_line_end(&line_end); gtk_text_view_get_iter_location( GTK_TEXT_VIEW(m_view), &line_end, &newline_location); if(buffer_x >= newline_location.x && buffer_y >= newline_location.y) { return false; } InfTextGtkBuffer* buffer = INF_TEXT_GTK_BUFFER( inf_session_get_buffer(INF_SESSION(m_session))); InfTextUser* author = inf_text_gtk_buffer_get_author(buffer, &iter); if(author != NULL) { tooltip->set_markup(Glib::ustring::compose( _("Text written by %1"), Glib::Markup::escape_text( inf_user_get_name(INF_USER(author))))); } else { tooltip->set_text(_("Unowned text")); } return true; } void Gobby::TextSessionView::on_view_style_updated() { GtkStyleContext* style = gtk_widget_get_style_context(GTK_WIDGET(m_view)); g_assert(style != NULL); // Get the view background, not the one for the side panels: gtk_style_context_save(style); gtk_style_context_add_class(style, GTK_STYLE_CLASS_VIEW); GdkRGBA rgba; gtk_style_context_get_background_color( style, GTK_STATE_FLAG_NORMAL, &rgba); gtk_style_context_restore(style); // Convert background color to HSV double h, s, v; gtk_rgb_to_hsv(rgba.red, rgba.green, rgba.blue, &h, &s, &v); // Adjust S and V for authorship colors s = s * 0.5 + 0.3; v = (std::pow(v + 1, 3) - 1) / 7 * 0.6 + 0.4; InfTextGtkBuffer* buffer = INF_TEXT_GTK_BUFFER( inf_session_get_buffer(INF_SESSION(m_session))); inf_text_gtk_buffer_set_saturation_value(buffer, s, v); } code/core/textsessionview.hpp000066400000000000000000000107771304552132100167240ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_TEXTSESSIONVIEW_HPP_ #define _GOBBY_TEXTSESSIONVIEW_HPP_ #include "core/sessionview.hpp" #include "core/textundogrouping.hpp" #include "core/preferences.hpp" #include #include #include #include #include #include #include namespace Gobby { class TextSessionView: public SessionView { public: typedef sigc::signal SignalLanguageChanged; TextSessionView(InfTextSession* session, const Glib::ustring& title, const Glib::ustring& path, const Glib::ustring& hostname, const std::string& info_storage_key, Preferences& preferences, GtkSourceLanguageManager* manager); ~TextSessionView(); InfTextSession* get_session() { return INF_TEXT_SESSION(m_session); } const std::string& get_info_storage_key() const { return m_info_storage_key; } void get_cursor_position(unsigned int& row, unsigned int& col) const; void set_selection(const GtkTextIter* begin, const GtkTextIter* end); Glib::ustring get_selected_text() const; void scroll_to_cursor_position(double within_margin); GtkSourceLanguage* get_language() const; void set_language(GtkSourceLanguage* language); // cannot overwrite with covariant return type InfTextUser because // C++ does not know GObject inheritance: virtual InfUser* get_active_user() const; void set_active_user(InfTextUser* user); // requires active user to be set: TextUndoGrouping& get_undo_grouping() { return *m_undo_grouping; } GtkSourceView* get_text_view() { return m_view; } GtkSourceBuffer* get_text_buffer() { return m_buffer; } SignalLanguageChanged signal_language_changed() const { return m_signal_language_changed; } protected: void on_user_color_changed(); void on_alpha_changed(); void on_show_remote_cursors_changed(); void on_show_remote_selections_changed(); void on_show_remote_current_lines_changed(); void on_show_remote_cursor_positions_changed(); void on_tab_width_changed(); void on_tab_spaces_changed(); void on_auto_indent_changed(); void on_homeend_smart_changed(); void on_wrap_mode_changed(); void on_linenum_display_changed(); void on_curline_highlight_changed(); void on_margin_display_changed(); void on_margin_pos_changed(); void on_bracket_highlight_changed(); void on_whitespace_display_changed(); void on_font_changed(); void on_scheme_changed(); void on_view_style_updated(); bool on_query_tooltip(int x, int y, bool keyboard_mode, const Glib::RefPtr& tooltip); static gboolean on_query_tooltip_static(GtkWidget* widget, int x, int y, gboolean keyboard_mode, GtkTooltip* tooltip, gpointer user_data) { return static_cast(user_data)-> on_query_tooltip(x, y, keyboard_mode, Glib::wrap(tooltip, true)); } static void on_view_style_updated_static(GtkWidget* view, gpointer user_data) { static_cast(user_data)-> on_view_style_updated(); } std::string m_info_storage_key; Preferences& m_preferences; Glib::RefPtr m_font_provider; GtkSourceView* m_view; GtkSourceBuffer* m_buffer; std::unique_ptr m_undo_grouping; InfTextGtkView* m_infview; InfTextGtkViewport* m_infviewport; SignalLanguageChanged m_signal_language_changed; }; } #endif // _GOBBY_TEXTSESSIONVIEW_HPP_ code/core/texttablabel.cpp000066400000000000000000000136451304552132100161040ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "core/texttablabel.hpp" #include "core/folder.hpp" Gobby::TextTabLabel::UserWatcher::UserWatcher(TextTabLabel* label, InfTextUser* user): m_label(label), m_user(user) { connect(); } Gobby::TextTabLabel::UserWatcher::UserWatcher(const UserWatcher& other): m_label(other.m_label), m_user(other.m_user) { connect(); } Gobby::TextTabLabel::UserWatcher::~UserWatcher() { g_signal_handler_disconnect(m_user, m_handle); } InfTextUser* Gobby::TextTabLabel::UserWatcher::get_user() const { return m_user; } bool Gobby::TextTabLabel::UserWatcher::operator==(InfTextUser* other_user) const { return m_user == other_user; } void Gobby::TextTabLabel::UserWatcher::connect() { m_handle = g_signal_connect( G_OBJECT(m_user), "notify::hue", G_CALLBACK(&UserWatcher::on_notify_hue), m_label); } void Gobby::TextTabLabel::UserWatcher::on_notify_hue(GObject* user_object, GParamSpec* spec, gpointer user_data) { static_cast(user_data)->update_dots(); } Gobby::TextTabLabel::TextTabLabel(Folder& folder, TextSessionView& view): TabLabel(folder, view, "text-x-generic"), m_dot_char(0) { m_modified_changed_handle = g_signal_connect_after( G_OBJECT(view.get_text_buffer()), "modified-changed", G_CALLBACK(on_modified_changed_static), this); InfTextBuffer* buffer = INF_TEXT_BUFFER( inf_session_get_buffer( INF_SESSION(view.get_session()))); m_insert_text_handle = g_signal_connect_after( G_OBJECT(buffer), "text-inserted", G_CALLBACK(on_text_inserted_static), this); m_erase_text_handle = g_signal_connect_after( G_OBJECT(buffer), "text-erased", G_CALLBACK(on_text_erased_static), this); insert_next_to(m_title, Gtk::POS_RIGHT); attach_next_to(m_dots, m_title, Gtk::POS_RIGHT, 1, 1); update_modified(); update_dot_char(); } Gobby::TextTabLabel::~TextTabLabel() { TextSessionView& text_view = dynamic_cast(m_view); g_signal_handler_disconnect(text_view.get_text_buffer(), m_modified_changed_handle); InfTextBuffer* buffer = INF_TEXT_BUFFER( inf_session_get_buffer( INF_SESSION(m_view.get_session()))); g_signal_handler_disconnect(buffer, m_erase_text_handle); g_signal_handler_disconnect(buffer, m_insert_text_handle); } void Gobby::TextTabLabel::on_style_updated() { TabLabel::on_style_updated(); update_dot_char(); } void Gobby::TextTabLabel::on_notify_status() { TabLabel::on_notify_status(); update_modified(); } void Gobby::TextTabLabel::on_activate() { TabLabel::on_activate(); m_changed_by.clear(); update_dots(); } void Gobby::TextTabLabel::on_modified_changed() { update_modified(); } void Gobby::TextTabLabel::on_changed(InfTextUser* author) { if(!m_changed) { InfSession* session = INF_SESSION(m_view.get_session()); if(inf_session_get_status(session) == INF_SESSION_RUNNING) set_changed(); } if(m_folder.get_current_document() != &m_view) { // TODO: remove dot if all the user's // new contributions where undone if(std::find(m_changed_by.begin(), m_changed_by.end(), author) == m_changed_by.end()) { m_changed_by.push_back(UserWatcher(this, author)); update_dots(); } } } void Gobby::TextTabLabel::update_modified() { InfSession* session = INF_SESSION(m_view.get_session()); bool modified = inf_buffer_get_modified(inf_session_get_buffer(session)); InfSessionStatus status = inf_session_get_status(session); if(status == INF_SESSION_SYNCHRONIZING || status == INF_SESSION_PRESYNC) { modified = false; } if(modified) m_title.set_text("*" + m_view.get_title()); else m_title.set_text(m_view.get_title()); } void Gobby::TextTabLabel::update_dot_char() { static const gunichar dot_chars[] = { 0x270E, /* pencil */ 0x26AB, /* medium black circle */ 0x25CF, /* black circle */ 0x002A, /* asterisk */ 0x0000 }; // Find a glyph for the user dots const gunichar* c; for(c = dot_chars; *c; ++c) { m_dots.set_text(Glib::ustring(1, *c)); if(m_dots.get_layout()->get_unknown_glyphs_count() == 0) break; } m_dot_char = *c; // Update dots using this char update_dots(); } void Gobby::TextTabLabel::update_dots() { g_assert(m_dot_char != 0); if (m_changed_by.empty()) { m_dots.hide(); } else { Glib::ustring markup; for(UserWatcherList::iterator iter = m_changed_by.begin(); iter != m_changed_by.end(); ++iter) { double hue = inf_text_user_get_hue(iter->get_user()); double sat = 0.6; double val = 0.6; double r, g, b; gtk_hsv_to_rgb(hue, sat, val, &r, &g, &b); const unsigned short red_i = static_cast(r * 65535.0 + 0.5); const unsigned short green_i = static_cast(g * 65535.0 + 0.5); const unsigned short blue_i = static_cast(b * 65535.0 + 0.5); gchar* markup_escaped = g_markup_printf_escaped( "&#%u;", red_i, green_i, blue_i, static_cast(m_dot_char)); markup = markup_escaped; g_free(markup_escaped); } m_dots.set_markup(markup); m_dots.show(); } } code/core/texttablabel.hpp000066400000000000000000000057331304552132100161100ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_TEXTTABLABEL_HPP_ #define _GOBBY_TEXTTABLABEL_HPP_ #include "core/textsessionview.hpp" #include "core/tablabel.hpp" namespace Gobby { class TextTabLabel: public TabLabel { public: TextTabLabel(Folder& folder, TextSessionView& view); ~TextTabLabel(); protected: static void on_modified_changed_static(GtkTextBuffer* buffer, gpointer user_data) { static_cast(user_data)->on_modified_changed(); } static void on_text_erased_static(InfTextBuffer* buffer, guint position, InfTextChunk* chunk, InfTextUser* author, gpointer user_data) { static_cast(user_data)->on_changed(author); } static void on_text_inserted_static(InfTextBuffer* buffer, guint position, InfTextChunk* text, InfTextUser* author, gpointer user_data) { static_cast(user_data)->on_changed(author); } virtual void on_style_updated(); virtual void on_notify_status(); // override virtual void on_activate(); void on_modified_changed(); void on_changed(InfTextUser* author); Gtk::Label m_dots; private: void update_modified(); void update_dot_char(); void update_dots(); gunichar m_dot_char; gulong m_modified_changed_handle; gulong m_erase_text_handle; gulong m_insert_text_handle; class UserWatcher { public: UserWatcher(TextTabLabel* label, InfTextUser* user); UserWatcher(const UserWatcher& other); ~UserWatcher(); InfTextUser* get_user() const; bool operator==(InfTextUser* other_user) const; private: void connect(); void disconnect(); static void on_notify_hue(GObject* user_object, GParamSpec* spec, gpointer user_data); TabLabel* m_label; InfTextUser* m_user; gulong m_handle; }; typedef std::list UserWatcherList; UserWatcherList m_changed_by; }; } #endif // _GOBBY_TEXTTABLABEL_HPP_ code/core/textundogrouping.cpp000066400000000000000000000047251304552132100170550ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "core/textundogrouping.hpp" Gobby::TextUndoGrouping::TextUndoGrouping(InfAdoptedAlgorithm* algorithm, InfTextUser* user, GtkTextBuffer* buffer): m_buffer(buffer), m_grouping(inf_text_undo_grouping_new()) { g_object_ref(m_buffer); inf_adopted_undo_grouping_set_algorithm( INF_ADOPTED_UNDO_GROUPING(m_grouping), algorithm, INF_ADOPTED_USER(user)); m_begin_user_action_handle = g_signal_connect( G_OBJECT(m_buffer), "begin-user-action", G_CALLBACK(on_begin_user_action_static), this); m_end_user_action_handle = g_signal_connect( G_OBJECT(m_buffer), "end-user-action", G_CALLBACK(on_end_user_action_static), this); } Gobby::TextUndoGrouping::~TextUndoGrouping() { g_signal_handler_disconnect(m_buffer, m_begin_user_action_handle); g_signal_handler_disconnect(m_buffer, m_end_user_action_handle); g_object_unref(m_grouping); g_object_unref(m_buffer); } guint Gobby::TextUndoGrouping::get_undo_size() const { return inf_adopted_undo_grouping_get_undo_size( INF_ADOPTED_UNDO_GROUPING(m_grouping)); } guint Gobby::TextUndoGrouping::get_redo_size() const { return inf_adopted_undo_grouping_get_redo_size( INF_ADOPTED_UNDO_GROUPING(m_grouping)); } void Gobby::TextUndoGrouping::on_begin_user_action() { // TODO: For paste actions we should not allow merging inf_adopted_undo_grouping_start_group( INF_ADOPTED_UNDO_GROUPING(m_grouping), TRUE); } void Gobby::TextUndoGrouping::on_end_user_action() { // TODO: For paste actions we should not allow merging inf_adopted_undo_grouping_end_group( INF_ADOPTED_UNDO_GROUPING(m_grouping), TRUE); } code/core/textundogrouping.hpp000066400000000000000000000037721304552132100170630ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_TEXTUNDOGROUPING_HPP_ #define _GOBBY_TEXTUNDOGROUPING_HPP_ #include #include #include #include namespace Gobby { class TextUndoGrouping { public: TextUndoGrouping(InfAdoptedAlgorithm* algorithm, InfTextUser* user, GtkTextBuffer* buffer); ~TextUndoGrouping(); guint get_undo_size() const; guint get_redo_size() const; InfTextUndoGrouping* get_inf_grouping() { return m_grouping; } protected: static void on_begin_user_action_static(GtkTextBuffer* buffer, gpointer user_data) { static_cast(user_data)-> on_begin_user_action(); } static void on_end_user_action_static(GtkTextBuffer* buffer, gpointer user_data) { static_cast(user_data)-> on_end_user_action(); } void on_begin_user_action(); void on_end_user_action(); GtkTextBuffer* m_buffer; InfTextUndoGrouping* m_grouping; gulong m_begin_user_action_handle; gulong m_end_user_action_handle; }; } #endif // _GOBBY_TEXTUNDOGROUPING_HPP_ code/core/titlebar.cpp000066400000000000000000000060761304552132100152370ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "core/titlebar.hpp" Gobby::TitleBar::TitleBar(Gtk::Window& window, const Folder& folder): m_window(window), m_folder(folder), m_current_view(NULL) { folder.signal_document_removed().connect( sigc::mem_fun(*this, &TitleBar::on_document_removed)); folder.signal_document_changed().connect( sigc::mem_fun(*this, &TitleBar::on_document_changed)); on_document_changed(folder.get_current_document()); } Gobby::TitleBar::~TitleBar() { on_document_changed(NULL); } void Gobby::TitleBar::on_document_removed(SessionView& view) { // TODO: Isn't this called by Folder already? if(m_current_view == &view) on_document_changed(NULL); } void Gobby::TitleBar::on_document_changed(SessionView* view) { if(m_current_view != NULL) { InfSession* session = m_current_view->get_session(); InfBuffer* buffer = inf_session_get_buffer(session); g_signal_handler_disconnect(G_OBJECT(session), m_notify_status_handler); g_signal_handler_disconnect(G_OBJECT(buffer), m_modified_changed_handler); } m_current_view = view; if(view != NULL) { InfSession* session = view->get_session(); InfBuffer* buffer = inf_session_get_buffer(session); m_notify_status_handler = g_signal_connect( G_OBJECT(session), "notify::status", G_CALLBACK(on_notify_status_static), this); m_modified_changed_handler = g_signal_connect( G_OBJECT(buffer), "notify::modified", G_CALLBACK(on_notify_modified_static), this); } update_title(); } void Gobby::TitleBar::on_notify_status() { update_title(); } void Gobby::TitleBar::on_notify_modified() { update_title(); } void Gobby::TitleBar::update_title() { // TODO: Show path, as gedit does. This requires change notification // for document info storage. if(m_current_view != NULL) { InfSession* session = m_current_view->get_session(); InfBuffer* buffer = inf_session_get_buffer(session); InfSessionStatus status = inf_session_get_status(session); if(status == INF_SESSION_SYNCHRONIZING || !inf_buffer_get_modified(buffer)) { m_window.set_title( m_current_view->get_title() + " - Gobby"); } else { m_window.set_title( "*" + m_current_view->get_title() + " - Gobby"); } } else { m_window.set_title("Gobby"); } } code/core/titlebar.hpp000066400000000000000000000037151304552132100152410ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_TITLEBAR_HPP_ #define _GOBBY_TITLEBAR_HPP_ #include "core/folder.hpp" #include "core/sessionview.hpp" #include #include namespace Gobby { class TitleBar: public sigc::trackable { public: TitleBar(Gtk::Window& window, const Folder& folder); ~TitleBar(); private: static void on_notify_status_static(GObject* object, GParamSpec* pspec, gpointer user_data) { static_cast(user_data)->on_notify_status(); } static void on_notify_modified_static(InfBuffer* buffer, GParamSpec* pspec, gpointer user_data) { static_cast(user_data)->on_notify_modified(); } void on_document_removed(SessionView& view); void on_document_changed(SessionView* view); void on_notify_status(); void on_notify_modified(); void update_title(); Gtk::Window& m_window; const Folder& m_folder; SessionView* m_current_view; gulong m_notify_status_handler; gulong m_modified_changed_handler; }; } #endif // _GOBBY_TITLEBAR_HPP_ code/core/toolbar.cpp000066400000000000000000000034731304552132100150710ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "core/toolbar.hpp" #include #include Gobby::Toolbar::Toolbar(const Preferences& preferences): m_preferences(preferences) { set_orientation(Gtk::ORIENTATION_VERTICAL); Glib::RefPtr builder = Gtk::Builder::create_from_resource( "/de/0x539/gobby/ui/toolbar.ui"); builder->get_widget("toolbar", m_toolbar); // Initial settings on_toolbar_style_changed(); on_show_toolbar_changed(); preferences.appearance.toolbar_style.signal_changed().connect( sigc::mem_fun(*this, &Toolbar::on_toolbar_style_changed)); preferences.appearance.show_toolbar.signal_changed().connect( sigc::mem_fun(*this, &Toolbar::on_show_toolbar_changed)); attach(*m_toolbar, 0, 0, 1, 1); } void Gobby::Toolbar::on_toolbar_style_changed() { m_toolbar->set_toolbar_style(m_preferences.appearance.toolbar_style); } void Gobby::Toolbar::on_show_toolbar_changed() { if(m_preferences.appearance.show_toolbar) m_toolbar->show(); else m_toolbar->hide(); } code/core/toolbar.hpp000066400000000000000000000023621304552132100150720ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_TOOLBAR_HPP_ #define _GOBBY_TOOLBAR_HPP_ #include "core/preferences.hpp" #include #include namespace Gobby { class Toolbar: public Gtk::Grid { public: Toolbar(const Preferences& preferences); private: void on_toolbar_style_changed(); void on_show_toolbar_changed(); const Preferences& m_preferences; Gtk::Toolbar* m_toolbar; }; } #endif // _GOBBY_TOOLBAR_HPP_ code/core/userjoin.cpp000066400000000000000000000157311304552132100152650ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "core/userjoin.hpp" #include #include #include namespace { void retr_local_user_func(InfUser* user, gpointer user_data) { (*static_cast(user_data)) = user; } std::vector::iterator find_name_param( std::vector& params) { for(std::vector::iterator iter = params.begin(); iter != params.end(); ++iter) { if(std::strcmp(iter->name, "name") == 0) return iter; } g_assert_not_reached(); return params.end(); } } Gobby::UserJoin::UserJoin(InfBrowser* browser, const InfBrowserIter* iter, InfSessionProxy* proxy, std::unique_ptr param_provider): m_node(browser, iter), m_proxy(proxy), m_param_provider(std::move(param_provider)), m_synchronization_complete_handler(0), m_request(NULL), m_retry_index(1), m_user(NULL), m_error(NULL) { g_object_ref(m_proxy); InfSession* session; g_object_get(G_OBJECT(proxy), "session", &session, NULL); if(inf_session_get_status(session) == INF_SESSION_SYNCHRONIZING) { // If not yet synchronized, wait for synchronization until // attempting userjoin m_synchronization_complete_handler = g_signal_connect_after( G_OBJECT(session), "synchronization-complete", G_CALLBACK(on_synchronization_complete_static), this); } else { // Delay this call to make sure we don't emit the // finished signal right inside the constructor. // TODO: This might not be a problem, since the caller // can just check for completion with the get_user() // and get_error() methods. Glib::signal_idle().connect( sigc::bind_return(sigc::mem_fun( *this, &UserJoin::attempt_user_join), false)); } g_object_unref(session); } Gobby::UserJoin::~UserJoin() { if(m_synchronization_complete_handler) { InfSession* session; g_object_get(G_OBJECT(m_proxy), "session", &session, NULL); g_signal_handler_disconnect( session, m_synchronization_complete_handler); g_object_unref(session); } if(m_request) { g_signal_handlers_disconnect_by_func( G_OBJECT(m_request), (gpointer)G_CALLBACK(on_user_join_finished_static), this); g_object_unref(m_request); // TODO: Keep watching the request, and when it finishes, make // the user unavailable. This should typically not be // necessary, because on the server side user join requests // finish immediately, and on the client side the only thing // that leads to the UserJoinInfo being deleted is when the // document is removed and we are unsubscribed from the // session, in which case we do not care about the user join // anymore anyway. // However, it would be good to handle this, just in case. } if(m_error != NULL) g_error_free(m_error); g_object_unref(m_proxy); } void Gobby::UserJoin::UserJoin::on_synchronization_complete() { // Disconnect signal handler, so that we don't get notified when // syncing this document in running state to another location // or server. InfSession* session; g_object_get(G_OBJECT(m_proxy), "session", &session, NULL); g_signal_handler_disconnect( session, m_synchronization_complete_handler); m_synchronization_complete_handler = 0; g_object_unref(session); // Attempt user join after synchronization attempt_user_join(); } void Gobby::UserJoin::on_user_join_finished(InfUser* user, const GError* error) { if(m_request != NULL) { g_signal_handlers_disconnect_by_func( G_OBJECT(m_request), (gpointer)G_CALLBACK(on_user_join_finished_static), this); g_object_unref(m_request); m_request = NULL; } if(error == NULL) { user_join_complete(user, error); } else if(error->domain == inf_user_error_quark() && error->code == INF_USER_ERROR_NAME_IN_USE) { // If name is in use retry with alternative user name ++m_retry_index; attempt_user_join(); } else { user_join_complete(user, error); } } void Gobby::UserJoin::attempt_user_join() { // Check if there is already a local user, for example for a // synced-in document. InfSession* session; g_object_get(G_OBJECT(m_proxy), "session", &session, NULL); InfUserTable* user_table = inf_session_get_user_table(session); InfUser* user = NULL; inf_user_table_foreach_local_user(user_table, retr_local_user_func, &user); g_object_unref(session); if(user != NULL) { user_join_complete(user, NULL); return; } // Next, check whether we are allowed to join a user if(m_node.get_browser() && m_node.get_browser_iter()) { InfBrowser* browser = m_node.get_browser(); const InfBrowserIter* iter = m_node.get_browser_iter(); const InfAclAccount* account = inf_browser_get_acl_local_account(browser); const InfAclAccountId acc_id = (account != NULL) ? account->id : 0; InfAclMask msk; inf_acl_mask_set1(&msk, INF_ACL_CAN_JOIN_USER); if(!inf_browser_check_acl(browser, iter, acc_id, &msk, NULL)) { GError* error = NULL; g_set_error( &error, inf_request_error_quark(), INF_REQUEST_ERROR_NOT_AUTHORIZED, "%s", inf_request_strerror( INF_REQUEST_ERROR_NOT_AUTHORIZED)); user_join_complete(NULL, error); g_error_free(error); return; } } // We are allowed, so attempt to join the user now. std::vector params = m_param_provider->get_user_join_parameters(); std::vector::iterator name_index = find_name_param(params); const gchar* name = g_value_get_string(&name_index->value); if(m_retry_index > 1) { gchar* new_name = g_strdup_printf( "%s %u", name, m_retry_index); g_value_take_string(&name_index->value, new_name); } GError* error = NULL; InfRequest* request = inf_session_proxy_join_user( m_proxy, params.size(), ¶ms[0], on_user_join_finished_static, this); for(unsigned int i = 0; i < params.size(); ++i) g_value_unset(¶ms[i].value); if(request != NULL) { m_request = request; g_object_ref(m_request); } } void Gobby::UserJoin::user_join_complete(InfUser* user, const GError* error) { g_assert(m_request == NULL); g_assert(m_user == NULL && m_error == NULL); m_user = user; if(error) m_error = g_error_copy(error); m_signal_finished.emit(m_user, error); } code/core/userjoin.hpp000066400000000000000000000062471304552132100152740ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_USER_JOIN_HPP_ #define _GOBBY_USER_JOIN_HPP_ #include "core/nodewatch.hpp" #include #include #include #include #include #include #include #include #include namespace Gobby { class UserJoin: public sigc::trackable { public: class ParameterProvider { public: ParameterProvider() {} virtual ~ParameterProvider() {} virtual std::vector get_user_join_parameters() = 0; }; typedef sigc::signal SignalFinished; UserJoin(InfBrowser* browser, const InfBrowserIter* iter, InfSessionProxy* proxy, std::unique_ptr param_provider); ~UserJoin(); InfSessionProxy* get_proxy() const { return m_proxy; } // If both are NULL, the user join is still in progress: InfUser* get_user() const { return m_user; } const GError* get_error() const { return m_error; } SignalFinished signal_finished() const { return m_signal_finished; } private: static void on_synchronization_complete_static(InfSession* session, InfXmlConnection* conn, gpointer user_data) { static_cast(user_data)-> on_synchronization_complete(); } static void on_user_join_finished_static(InfRequest* request, const InfRequestResult* res, const GError* error, gpointer user_data) { InfUser* user = NULL; if(error == NULL) inf_request_result_get_join_user(res, NULL, &user); static_cast(user_data)-> on_user_join_finished(user, error); } void on_synchronization_complete(); void on_user_join_finished(InfUser* user, const GError* error); void attempt_user_join(); void user_join_complete(InfUser* user, const GError* error); NodeWatch m_node; InfSessionProxy* m_proxy; std::unique_ptr m_param_provider; gulong m_synchronization_complete_handler; InfRequest* m_request; guint m_retry_index; InfUser* m_user; GError* m_error; SignalFinished m_signal_finished; }; } #endif // _GOBBY_USER_JOIN_HPP_ code/core/userlist.cpp000066400000000000000000000260301304552132100152730ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "core/userlist.hpp" #include "util/i18n.hpp" #include #include #include namespace { typedef sigc::slot ForeachUserFunc; void foreach_user_ctor_func(InfUser* user, gpointer user_data) { (*static_cast(user_data))(user); } Glib::RefPtr generate_user_color_pixbuf(Gtk::Widget& w, gdouble hue) { Glib::RefPtr icon_theme = Gtk::IconTheme::get_for_screen(w.get_screen()); Glib::RefPtr pixbuf; try { pixbuf = icon_theme->load_icon( "user-color-indicator", 16); } catch(const Glib::Error& ex) { // Icon not found // TODO: Check error domain and code pixbuf = icon_theme->load_icon( "image-missing", 16); } // pixbuf is shared, though we want to mess with it here pixbuf = pixbuf->copy(); for(int y = 0; y < pixbuf->get_height(); ++y) { for(int x = 0; x < pixbuf->get_width(); ++x) { guint8* pixels = pixbuf->get_pixels(); guint8* pixel = pixels + y * pixbuf->get_rowstride() + x * pixbuf->get_n_channels(); double r = pixel[0]/255.0; double g = pixel[1]/255.0; double b = pixel[2]/255.0; // Replace hue value double h, s, v; gtk_rgb_to_hsv(r, g, b, &h, &s, &v); gtk_hsv_to_rgb(hue, s, v, &r, &g, &b); pixel[0] = static_cast(r * 255.0 + 0.5); pixel[1] = static_cast(g * 255.0 + 0.5); pixel[2] = static_cast(b * 255.0 + 0.5); } } return pixbuf; } } Gobby::UserList::UserList(InfUserTable* table): m_table(table), m_store(Gtk::ListStore::create(m_columns)), m_view(m_store) { m_store->set_sort_func(m_columns.user, sigc::mem_fun(*this, &UserList::sort_func)); m_store->set_sort_column(m_columns.user, Gtk::SORT_ASCENDING); m_add_user_handle = g_signal_connect( G_OBJECT(table), "add-user", G_CALLBACK(on_add_user_static), this); ForeachUserFunc slot(sigc::mem_fun(*this, &UserList::on_add_user)); inf_user_table_foreach_user(table, foreach_user_ctor_func, &slot); Gtk::CellRendererPixbuf* icon_renderer = Gtk::manage(new Gtk::CellRendererPixbuf); Gtk::CellRendererPixbuf* color_renderer = Gtk::manage(new Gtk::CellRendererPixbuf); Gtk::CellRendererText* name_renderer = Gtk::manage(new Gtk::CellRendererText); Gtk::TreeViewColumn* column = Gtk::manage(new Gtk::TreeViewColumn(_("Users"))); column->pack_start(*icon_renderer, false); column->pack_start(*color_renderer, false); column->pack_start(*name_renderer, true); column->set_cell_data_func( *icon_renderer, sigc::mem_fun(*this, &UserList::icon_cell_data_func)); column->set_cell_data_func( *color_renderer, sigc::mem_fun(*this, &UserList::color_cell_data_func)); column->set_cell_data_func( *name_renderer, sigc::mem_fun(*this, &UserList::name_cell_data_func)); m_view.signal_row_activated().connect( sigc::mem_fun(*this, &UserList::on_row_activated)); column->set_spacing(6); m_view.append_column(*column); m_view.get_selection()->set_mode(Gtk::SELECTION_NONE); m_view.set_headers_visible(false); m_view.show(); Gtk::ScrolledWindow* scroll = Gtk::manage(new Gtk::ScrolledWindow); scroll->set_shadow_type(Gtk::SHADOW_IN); scroll->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); scroll->set_hexpand(true); scroll->set_vexpand(true); scroll->add(m_view); scroll->show(); attach(*scroll, 0, 0, 1, 1); } Gobby::UserList::~UserList() { g_signal_handler_disconnect(G_OBJECT(m_table), m_add_user_handle); m_filter_model.reset(); const Gtk::TreeModel::Children& children = m_store->children(); for(Gtk::TreeIter iter = children.begin(); iter != children.end(); ++ iter) { InfUser* user = (*iter)[m_columns.user]; gulong notify_hue_handle = (*iter)[m_columns.notify_hue_handle]; gulong notify_status_handle = (*iter)[m_columns.notify_status_handle]; if(notify_hue_handle > 0) g_signal_handler_disconnect(G_OBJECT(user), notify_hue_handle); g_signal_handler_disconnect(G_OBJECT(user), notify_status_handle); } } void Gobby::UserList::set_show_disconnected(bool show_disconnected) { if(show_disconnected) { m_filter_model.reset(); m_view.set_model(m_store); } else { m_filter_model = Gtk::TreeModelFilter::create(m_store); m_view.set_model(m_filter_model); m_filter_model->set_visible_func( sigc::mem_fun(*this, &UserList::visible_func)); } } bool Gobby::UserList::visible_func(const Gtk::TreeIter& iter) { InfUser* user = (*iter)[m_columns.user]; // Can happen after creation of the node when the user object has // not yet been set if(user == NULL) return false; return inf_user_get_status(user) != INF_USER_UNAVAILABLE; } void Gobby::UserList::icon_cell_data_func(Gtk::CellRenderer* renderer, const Gtk::TreeIter& iter) { Gtk::CellRendererPixbuf* pixbuf_renderer = dynamic_cast(renderer); g_assert(pixbuf_renderer); pixbuf_renderer->property_stock_size() = Gtk::ICON_SIZE_MENU; InfUser* user = (*iter)[m_columns.user]; if(user == NULL) { // Can happen after creation of the node when the user // object has not yet been set pixbuf_renderer->property_visible() = false; } else { pixbuf_renderer->property_visible() = true; switch(inf_user_get_status(user)) { case INF_USER_ACTIVE: case INF_USER_INACTIVE: pixbuf_renderer->property_icon_name() = "user-available"; break; case INF_USER_UNAVAILABLE: pixbuf_renderer->property_icon_name() = "user-offline"; break; default: g_assert_not_reached(); break; } } } void Gobby::UserList::color_cell_data_func(Gtk::CellRenderer* renderer, const Gtk::TreeIter& iter) { Gtk::CellRendererPixbuf* pixbuf_renderer = dynamic_cast(renderer); g_assert(pixbuf_renderer); Glib::RefPtr pixbuf = (*iter)[m_columns.color]; if(pixbuf) { pixbuf_renderer->property_pixbuf() = pixbuf; pixbuf_renderer->property_visible() = true; } else { pixbuf_renderer->property_visible() = false; } } void Gobby::UserList::name_cell_data_func(Gtk::CellRenderer* renderer, const Gtk::TreeIter& iter) { Gtk::CellRendererText* text_renderer = dynamic_cast(renderer); g_assert(text_renderer); InfUser* user = (*iter)[m_columns.user]; if(user == NULL) { // Can happen after creation of the node when the user // object has not yet been set text_renderer->property_visible() = false; } else { switch(inf_user_get_status(INF_USER(user))) { case INF_USER_ACTIVE: text_renderer->property_foreground_set() = false; break; case INF_USER_INACTIVE: text_renderer->property_foreground() = "#606060"; break; case INF_USER_UNAVAILABLE: text_renderer->property_foreground() = "#a0a0a0"; break; } text_renderer->property_visible() = true; text_renderer->property_text() = inf_user_get_name(user); } } int Gobby::UserList::sort_func(const Gtk::TreeIter& iter1, const Gtk::TreeIter& iter2) { InfUser* user1 = (*iter1)[m_columns.user]; InfUser* user2 = (*iter2)[m_columns.user]; bool available1 = inf_user_get_status(user1) != INF_USER_UNAVAILABLE; bool available2 = inf_user_get_status(user2) != INF_USER_UNAVAILABLE; if(available1 != available2) { if(!available1) return 1; return -1; } else { // We might want to cache collate keys in the ListStore if // this turns out to be a performance problem: return g_utf8_collate(inf_user_get_name(user1), inf_user_get_name(user2)); } } void Gobby::UserList::on_add_user(InfUser* user) { g_assert(find_user_iter(user) == m_store->children().end()); Gtk::TreeIter iter = m_store->append(); (*iter)[m_columns.user] = user; (*iter)[m_columns.notify_status_handle] = g_signal_connect( G_OBJECT(user), "notify::status", G_CALLBACK(on_notify_status_static), this); if(INF_TEXT_IS_USER(user)) { Glib::RefPtr color_pixbuf = generate_user_color_pixbuf( *this, inf_text_user_get_hue(INF_TEXT_USER(user))); (*iter)[m_columns.color] = color_pixbuf; (*iter)[m_columns.notify_hue_handle] = g_signal_connect( G_OBJECT(user), "notify::hue", G_CALLBACK(on_notify_hue_static), this); } else { // Should be 0 anyway, but let's be sure: (*iter)[m_columns.notify_hue_handle] = 0; } } void Gobby::UserList::on_notify_hue(InfTextUser* user) { Gtk::TreeIter iter = find_user_iter(INF_USER(user)); g_assert(iter != m_store->children().end()); (*iter)[m_columns.color] = generate_user_color_pixbuf( *this, inf_text_user_get_hue(user)); } void Gobby::UserList::on_notify_status(InfUser* user) { Gtk::TreeIter iter = find_user_iter(user); g_assert(iter != m_store->children().end()); // This does not cause a resort: //m_store->row_changed(m_store->get_path(iter), iter); // But this does: (*iter)[m_columns.user] = user; } void Gobby::UserList::on_row_activated(const Gtk::TreePath& path, Gtk::TreeViewColumn* column) { Gtk::TreePath parent_path; if(m_filter_model) parent_path = m_filter_model->convert_path_to_child_path(path); else parent_path = path; const Gtk::TreeIter& iter = m_store->get_iter(path); InfUser* user = (*iter)[m_columns.user]; if(inf_user_get_status(user) != INF_USER_UNAVAILABLE) m_signal_user_activated.emit(user); } void Gobby::UserList::on_style_updated() { // Re-render all user color pixbufs, since the icon might have changed // with the new style. const Gtk::TreeModel::Children& children = m_store->children(); for(Gtk::TreeIter iter = children.begin(); iter != children.end(); ++ iter) { InfUser* user = (*iter)[m_columns.user]; if(INF_TEXT_IS_USER(user)) { (*iter)[m_columns.color] = generate_user_color_pixbuf( *this, inf_text_user_get_hue(INF_TEXT_USER(user))); } } } Gtk::TreeIter Gobby::UserList::find_user_iter(InfUser* user) { const Gtk::TreeModel::Children& children = m_store->children(); for(Gtk::TreeIter iter = children.begin(); iter != children.end(); ++ iter) { if(user == (*iter)[m_columns.user]) return iter; } return children.end(); } code/core/userlist.hpp000066400000000000000000000071201304552132100152770ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_USERLIST_HPP_ #define _GOBBY_USERLIST_HPP_ #include #include #include #include #include #include namespace Gobby { class UserList: public Gtk::Grid { public: typedef sigc::signal SignalUserActivated; UserList(InfUserTable* table); ~UserList(); void set_show_disconnected(bool show_disconnected); SignalUserActivated signal_user_activated() const { return m_signal_user_activated; } protected: InfUserTable* m_table; class Columns: public Gtk::TreeModelColumnRecord { public: Gtk::TreeModelColumn user; Gtk::TreeModelColumn > color; Gtk::TreeModelColumn notify_hue_handle; Gtk::TreeModelColumn notify_status_handle; Columns() { add(user); add(color); add(notify_hue_handle); add(notify_status_handle); } }; static void on_add_user_static(InfUserTable* user_table, InfUser* user, gpointer user_data) { static_cast(user_data)-> on_add_user(user); } static void on_notify_status_static(InfUser* user, GParamSpec* pspec, gpointer user_data) { static_cast(user_data)-> on_notify_status(user); } static void on_notify_hue_static(InfUser* user, GParamSpec* pspec, gpointer user_data) { static_cast(user_data)-> on_notify_hue(INF_TEXT_USER(user)); } bool visible_func(const Gtk::TreeIter& iter); void icon_cell_data_func(Gtk::CellRenderer* renderer, const Gtk::TreeIter& iter); void color_cell_data_func(Gtk::CellRenderer* renderer, const Gtk::TreeIter& iter); void name_cell_data_func(Gtk::CellRenderer* renderer, const Gtk::TreeIter& iter); int sort_func(const Gtk::TreeIter& iter1, const Gtk::TreeIter& iter2); void on_add_user(InfUser* user); void on_notify_status(InfUser* user); void on_notify_hue(InfTextUser* user); //void on_select_func(const Gtk::TreeIter& iter); void on_row_activated(const Gtk::TreePath& path, Gtk::TreeViewColumn* column); virtual void on_style_updated(); Gtk::TreeIter find_user_iter(InfUser* user); Columns m_columns; Glib::RefPtr m_store; Glib::RefPtr m_filter_model; Gtk::TreeView m_view; gulong m_add_user_handle; SignalUserActivated m_signal_user_activated; }; } #endif // _GOBBY_ICON_HPP_ code/core/windowactions.cpp000066400000000000000000000051771304552132100163220ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "windowactions.hpp" Gobby::WindowActions::WindowActions(Gio::ActionMap& map, const Preferences& preferences): new_document(map.add_action("new")), open(map.add_action("open")), open_location(map.add_action("open-location")), save(map.add_action("save")), save_as(map.add_action("save-as")), save_all(map.add_action("save-all")), export_html(map.add_action("export-html")), connect(map.add_action("connect")), close(map.add_action("close")), undo(map.add_action("undo")), redo(map.add_action("redo")), cut(map.add_action("cut")), copy(map.add_action("copy")), paste(map.add_action("paste")), find(map.add_action("find")), find_next(map.add_action("find-next")), find_prev(map.add_action("find-prev")), find_replace(map.add_action("find-replace")), goto_line(map.add_action("goto-line")), hide_user_colors(map.add_action("hide-user-colors")), fullscreen(map.add_action_bool("fullscreen", false)), zoom_in(map.add_action("zoom-in")), zoom_out(map.add_action("zoom-out")), view_toolbar(map.add_action_bool( "view-toolbar", static_cast(preferences.appearance.show_toolbar))), view_statusbar(map.add_action_bool( "view-statusbar", static_cast(preferences.appearance.show_statusbar))), view_browser(map.add_action_bool( "view-browser", static_cast(preferences.appearance.show_browser))), view_chat(map.add_action_bool( "view-chat", static_cast(preferences.appearance.show_chat))), view_document_userlist(map.add_action_bool( "view-document-userlist", static_cast( preferences.appearance.show_document_userlist))), view_chat_userlist(map.add_action_bool( "view-chat-userlist", static_cast( preferences.appearance.show_chat_userlist))), highlight_mode(map.add_action_radio_string("highlight-mode", "")) { } code/core/windowactions.hpp000066400000000000000000000051271304552132100163220ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_WINDOWACTIONS_HPP_ #define _GOBBY_WINDOWACTIONS_HPP_ #include "core/preferences.hpp" #include namespace Gobby { class WindowActions { public: WindowActions(Gio::ActionMap& map, const Preferences& preferences); const Glib::RefPtr new_document; const Glib::RefPtr open; const Glib::RefPtr open_location; const Glib::RefPtr save; const Glib::RefPtr save_as; const Glib::RefPtr save_all; const Glib::RefPtr export_html; const Glib::RefPtr connect; const Glib::RefPtr close; const Glib::RefPtr undo; const Glib::RefPtr redo; const Glib::RefPtr cut; const Glib::RefPtr copy; const Glib::RefPtr paste; const Glib::RefPtr find; const Glib::RefPtr find_next; const Glib::RefPtr find_prev; const Glib::RefPtr find_replace; const Glib::RefPtr goto_line; const Glib::RefPtr hide_user_colors; const Glib::RefPtr fullscreen; const Glib::RefPtr zoom_in; const Glib::RefPtr zoom_out; const Glib::RefPtr view_toolbar; const Glib::RefPtr view_statusbar; const Glib::RefPtr view_browser; const Glib::RefPtr view_chat; const Glib::RefPtr view_document_userlist; const Glib::RefPtr view_chat_userlist; const Glib::RefPtr highlight_mode; }; } #endif // _GOBBY_WINDOWACTIONS_HPP_ code/dialogs/000077500000000000000000000000001304552132100134065ustar00rootroot00000000000000code/dialogs/Makefile.am000066400000000000000000000014241304552132100154430ustar00rootroot00000000000000gobby_0_5_SOURCES += \ code/dialogs/connection-dialog.cpp \ code/dialogs/connection-info-dialog.cpp \ code/dialogs/document-location-dialog.cpp \ code/dialogs/entry-dialog.cpp \ code/dialogs/find-dialog.cpp \ code/dialogs/goto-dialog.cpp \ code/dialogs/initial-dialog.cpp \ code/dialogs/open-location-dialog.cpp \ code/dialogs/password-dialog.cpp \ code/dialogs/preferences-dialog.cpp noinst_HEADERS += \ code/dialogs/connection-dialog.hpp \ code/dialogs/connection-info-dialog.hpp \ code/dialogs/document-location-dialog.hpp \ code/dialogs/entry-dialog.hpp \ code/dialogs/find-dialog.hpp \ code/dialogs/goto-dialog.hpp \ code/dialogs/initial-dialog.hpp \ code/dialogs/open-location-dialog.hpp \ code/dialogs/password-dialog.hpp \ code/dialogs/preferences-dialog.hpp code/dialogs/connection-dialog.cpp000066400000000000000000000037251304552132100175150ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "dialogs/connection-dialog.hpp" #include "util/i18n.hpp" // TODO: Merge this with entrydialog and passworddialog to a slightly more // generic entry dialog. Gobby::ConnectionDialog::ConnectionDialog( GtkDialog* cobject, const Glib::RefPtr& builder) : Gtk::Dialog(cobject) { builder->get_widget("entry", m_entry); } std::unique_ptr Gobby::ConnectionDialog::create(Gtk::Window& parent) { Glib::RefPtr builder = Gtk::Builder::create_from_resource( "/de/0x539/gobby/ui/connection-dialog.ui"); ConnectionDialog* dialog; builder->get_widget_derived("ConnectionDialog", dialog); dialog->set_transient_for(parent); return std::unique_ptr(dialog); } Glib::ustring Gobby::ConnectionDialog::get_host_name() const { return m_entry->get_text(); } void Gobby::ConnectionDialog::on_show() { Gtk::Dialog::on_show(); // We can't do this in the constructor, because the buttons are added // by the caller after the widget has been constructed. set_default_response(Gtk::RESPONSE_ACCEPT); m_entry->select_region(0, m_entry->get_text().length()); m_entry->grab_focus(); } code/dialogs/connection-dialog.hpp000066400000000000000000000027511304552132100175200ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_CONNECTIONDIALOG_HPP_ #define _GOBBY_CONNECTIONDIALOG_HPP_ #include #include #include namespace Gobby { class ConnectionDialog: public Gtk::Dialog { private: friend class Gtk::Builder; ConnectionDialog(GtkDialog* cobject, const Glib::RefPtr& builder); public: static std::unique_ptr create(Gtk::Window& parent); Glib::ustring get_host_name() const; protected: virtual void on_show(); Gtk::Entry* m_entry; // TODO: Use HistoryComboBox here, with the same history as the direct connection box }; } #endif // _GOBBY_CONNECTIONDIALOG_HPP_ code/dialogs/connection-info-dialog.cpp000066400000000000000000000215711304552132100204450ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "dialogs/connection-info-dialog.hpp" #include "util/i18n.hpp" #include Gobby::ConnectionInfoDialog::ConnectionInfoDialog( GtkDialog* cobject, const Glib::RefPtr& builder) : Gtk::Dialog(cobject), m_browser(NULL), m_connection_store(Gtk::ListStore::create(m_columns)), m_connection_added_handler(0), m_connection_removed_handler(0), m_empty(true) { builder->get_widget("image", m_image); builder->get_widget("treeview", m_connection_tree_view); builder->get_widget("scrolled-window", m_connection_scroll); m_connection_view = INF_GTK_CONNECTION_VIEW( gtk_builder_get_object(builder->gobj(), "connection-info")); m_connection_tree_view->set_model(m_connection_store); Gtk::CellRendererPixbuf* icon_renderer = Gtk::manage(new Gtk::CellRendererPixbuf); Gtk::CellRendererText* name_renderer = Gtk::manage(new Gtk::CellRendererText); Gtk::TreeViewColumn* column = Gtk::manage(new Gtk::TreeViewColumn(_("Connections"))); column->pack_start(*icon_renderer, false); column->pack_start(*name_renderer, true); column->set_cell_data_func( *icon_renderer, sigc::mem_fun(*this, &ConnectionInfoDialog::icon_cell_data_func)); column->set_cell_data_func( *name_renderer, sigc::mem_fun(*this, &ConnectionInfoDialog::name_cell_data_func)); m_connection_tree_view->append_column(*column); Glib::RefPtr selection = m_connection_tree_view->get_selection(); selection->signal_changed().connect( sigc::mem_fun(*this, &ConnectionInfoDialog::on_selection_changed)); } Gobby::ConnectionInfoDialog::~ConnectionInfoDialog() { set_browser(NULL); } std::unique_ptr Gobby::ConnectionInfoDialog::create(Gtk::Window& parent, InfBrowser* browser) { // Make sure the GType for InfGtkConnectionView is registered, // since the UI definition contains a widget of this kind, and // GtkBuilder will want to look it up. // Also, make sure the call does not get optimized out. GType type = inf_gtk_connection_view_get_type(); if(type == 0) throw std::logic_error("inf_gtk_connection_view_get_type"); Glib::RefPtr builder = Gtk::Builder::create_from_resource( "/de/0x539/gobby/ui/connection-info-dialog.ui"); ConnectionInfoDialog* dialog_ptr; builder->get_widget_derived("ConnectionInfoDialog", dialog_ptr); std::unique_ptr dialog(dialog_ptr); dialog->set_transient_for(parent); dialog->set_browser(browser); return dialog; } void Gobby::ConnectionInfoDialog::set_browser(InfBrowser* browser) { if(m_browser != NULL) { if(m_connection_added_handler != 0) { g_signal_handler_disconnect( G_OBJECT(m_browser), m_connection_added_handler); } if(m_connection_removed_handler != 0) { g_signal_handler_disconnect( G_OBJECT(m_browser), m_connection_removed_handler); } g_object_unref(m_browser); } m_browser = browser; if(m_browser != NULL) g_object_ref(m_browser); if(INFC_IS_BROWSER(browser)) { m_empty = false; InfXmlConnection* conn = infc_browser_get_connection( INFC_BROWSER(browser)); if(INF_IS_XMPP_CONNECTION(conn)) { inf_gtk_connection_view_set_connection( m_connection_view, INF_XMPP_CONNECTION(conn)); } /* TODO: Show this corresponding to connection status, or * network-server if we are a server. */ m_image->set_from_icon_name( "network-idle", Gtk::ICON_SIZE_DIALOG); m_connection_scroll->hide(); } else if(INFD_IS_DIRECTORY(browser)) { m_connection_store->clear(); m_empty = true; infd_directory_foreach_connection( INFD_DIRECTORY(browser), &ConnectionInfoDialog::foreach_connection_func_static, this); m_connection_added_handler = g_signal_connect( G_OBJECT(browser), "connection-added", G_CALLBACK(on_connection_added_static), this); m_connection_removed_handler = g_signal_connect( G_OBJECT(browser), "connection-removed", G_CALLBACK(on_connection_removed_static), this); Glib::RefPtr selection = m_connection_tree_view->get_selection(); if(m_empty) { selection->set_mode(Gtk::SELECTION_NONE); Gtk::TreeIter iter = m_connection_store->append(); (*iter)[m_columns.connection] = NULL; } else { selection->set_mode(Gtk::SELECTION_BROWSE); } m_image->set_from_icon_name( "network-server", Gtk::ICON_SIZE_DIALOG); } if(!m_empty) gtk_widget_show(GTK_WIDGET(m_connection_view)); else gtk_widget_hide(GTK_WIDGET(m_connection_view)); } void Gobby::ConnectionInfoDialog::foreach_connection_func( InfXmlConnection* conn) { if(INF_IS_XMPP_CONNECTION(conn)) { g_assert(find_connection(INF_XMPP_CONNECTION(conn)) == m_connection_store->children().end()); if(m_empty) m_connection_store->clear(); Gtk::TreeIter iter = m_connection_store->append(); (*iter)[m_columns.connection] = INF_XMPP_CONNECTION(conn); if(m_empty) { gtk_widget_show(GTK_WIDGET(m_connection_view)); m_connection_tree_view->get_selection()-> set_mode(Gtk::SELECTION_BROWSE); m_connection_tree_view->get_selection()->select(iter); m_empty = false; } } } void Gobby::ConnectionInfoDialog::on_connection_added( InfXmlConnection* conn) { if(INF_IS_XMPP_CONNECTION(conn)) { g_assert(find_connection(INF_XMPP_CONNECTION(conn)) == m_connection_store->children().end()); if(m_empty) m_connection_store->clear(); Gtk::TreeIter iter = m_connection_store->append(); (*iter)[m_columns.connection] = INF_XMPP_CONNECTION(conn); if(m_empty) { gtk_widget_show(GTK_WIDGET(m_connection_view)); m_connection_tree_view->get_selection()-> set_mode(Gtk::SELECTION_BROWSE); m_connection_tree_view->get_selection()->select(iter); m_empty = false; } } } void Gobby::ConnectionInfoDialog::on_connection_removed( InfXmlConnection* conn) { if(INF_IS_XMPP_CONNECTION(conn)) { Gtk::TreeIter iter = find_connection(INF_XMPP_CONNECTION(conn)); g_assert(iter != m_connection_store->children().end()); m_connection_store->erase(iter); g_assert(!m_empty); if(m_connection_store->children().empty()) { Gtk::TreeIter iter = m_connection_store->append(); (*iter)[m_columns.connection] = NULL; gtk_widget_hide(GTK_WIDGET(m_connection_view)); m_connection_tree_view->get_selection()-> set_mode(Gtk::SELECTION_NONE); m_empty = true; } } } void Gobby::ConnectionInfoDialog::on_selection_changed() { if(m_connection_tree_view->get_selection()->count_selected_rows() > 0) { Gtk::TreeIter iter = m_connection_tree_view->get_selection()-> get_selected(); inf_gtk_connection_view_set_connection( m_connection_view, (*iter)[m_columns.connection]); } else { inf_gtk_connection_view_set_connection( m_connection_view, NULL); } } void Gobby::ConnectionInfoDialog::icon_cell_data_func( Gtk::CellRenderer* renderer, const Gtk::TreeIter& iter) { Gtk::CellRendererPixbuf* pixbuf_renderer = dynamic_cast(renderer); g_assert(pixbuf_renderer); InfXmppConnection* conn = (*iter)[m_columns.connection]; // TODO: stock_size? pixbuf_renderer->property_icon_name() = "network-idle"; pixbuf_renderer->property_visible() = (conn != NULL); } void Gobby::ConnectionInfoDialog::name_cell_data_func( Gtk::CellRenderer* renderer, const Gtk::TreeIter& iter) { Gtk::CellRendererText* text_renderer = dynamic_cast(renderer); g_assert(text_renderer); gchar* hostname; InfXmppConnection* conn = (*iter)[m_columns.connection]; if(conn != NULL) { g_object_get( G_OBJECT(conn), "remote-hostname", &hostname, NULL); text_renderer->property_text() = hostname; g_free(hostname); } else { text_renderer->property_text() = _("Nobody is connected to this computer"); text_renderer->property_wrap_width() = 170; } text_renderer->property_visible() = true; } Gtk::TreeIter Gobby::ConnectionInfoDialog::find_connection( InfXmppConnection* conn) { const Gtk::TreeModel::Children& children = m_connection_store->children(); for(Gtk::TreeIter iter = children.begin(); iter != children.end(); ++iter) { if(conn == (*iter)[m_columns.connection]) return iter; } return children.end(); } code/dialogs/connection-info-dialog.hpp000066400000000000000000000065261304552132100204550ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_CONNECTIONINFODIALOG_HPP_ #define _GOBBY_CONNECTIONINFODIALOG_HPP_ #include #include #include #include #include #include #include #include namespace Gobby { class ConnectionInfoDialog: public Gtk::Dialog { friend class Gtk::Builder; ConnectionInfoDialog(GtkDialog* cobject, const Glib::RefPtr& builder); public: ~ConnectionInfoDialog(); static std::unique_ptr create( Gtk::Window& parent, InfBrowser* browser); void set_browser(InfBrowser* browser); private: static void foreach_connection_func_static(InfXmlConnection* conn, gpointer user_data) { static_cast(user_data)-> foreach_connection_func(conn); } static void on_connection_added_static(InfdDirectory* directory, InfXmlConnection* conn, gpointer user_data) { static_cast(user_data)-> on_connection_added(conn); } static void on_connection_removed_static(InfdDirectory* directory, InfXmlConnection* conn, gpointer user_data) { static_cast(user_data)-> on_connection_removed(conn); } void foreach_connection_func(InfXmlConnection* conn); void on_connection_added(InfXmlConnection* conn); void on_connection_removed(InfXmlConnection* conn); void on_selection_changed(); void icon_cell_data_func(Gtk::CellRenderer* renderer, const Gtk::TreeIter& iter); void name_cell_data_func(Gtk::CellRenderer* renderer, const Gtk::TreeIter& iter); protected: Gtk::TreeIter find_connection(InfXmppConnection* conn); class Columns: public Gtk::TreeModelColumnRecord { public: Gtk::TreeModelColumn connection; Columns() { add(connection); } }; InfBrowser* m_browser; Columns m_columns; Glib::RefPtr m_connection_store; Gtk::Image* m_image; Gtk::TreeView* m_connection_tree_view; Gtk::ScrolledWindow* m_connection_scroll; InfGtkConnectionView* m_connection_view; gulong m_connection_added_handler; gulong m_connection_removed_handler; bool m_empty; }; } #endif // _GOBBY_CONNECTIONINFODIALOG_HPP_ code/dialogs/document-location-dialog.cpp000066400000000000000000000172721304552132100210040ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "dialogs/document-location-dialog.hpp" #include "util/i18n.hpp" Gobby::DocumentLocationDialog::DocumentLocationDialog( GtkDialog* cobject, const Glib::RefPtr& builder) : Gtk::Dialog(cobject), m_filter_model(NULL) { builder->get_widget("document-name-label", m_name_label); builder->get_widget("document-name", m_name_entry); builder->get_widget("location-label", m_location_label); m_view = INF_GTK_BROWSER_VIEW( gtk_builder_get_object(GTK_BUILDER(builder->gobj()), "view")); g_signal_connect(m_view, "selection-changed", G_CALLBACK(on_selection_changed_static), this); add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL); add_button(_("_Open"), Gtk::RESPONSE_ACCEPT); set_response_sensitive(Gtk::RESPONSE_ACCEPT, false); set_default_response(Gtk::RESPONSE_ACCEPT); } std::unique_ptr Gobby::DocumentLocationDialog::create(Gtk::Window& parent, InfGtkBrowserModel* model) { Glib::RefPtr builder = Gtk::Builder::create_from_resource( "/de/0x539/gobby/ui/document-location-dialog.ui"); DocumentLocationDialog* dialog_ptr = NULL; builder->get_widget_derived("DocumentLocationDialog", dialog_ptr); std::unique_ptr dialog(dialog_ptr); dialog->set_transient_for(parent); dialog->m_filter_model = inf_gtk_browser_model_filter_new(model); gtk_tree_view_set_model( GTK_TREE_VIEW(dialog->m_view), GTK_TREE_MODEL(dialog->m_filter_model)); g_signal_connect(dialog->m_filter_model, "row-changed", G_CALLBACK(on_row_changed_static), dialog.get()); // Hide non-subdirectories: gtk_tree_model_filter_set_visible_func( GTK_TREE_MODEL_FILTER(dialog->m_filter_model), filter_visible_func_static, dialog.get(), NULL); // Required to filter initial content: gtk_tree_model_filter_refilter( GTK_TREE_MODEL_FILTER(dialog->m_filter_model)); // Default is single document mode: dialog->set_single_document_mode(); return dialog; } Gobby::DocumentLocationDialog::~DocumentLocationDialog() { g_object_unref(m_filter_model); m_filter_model = NULL; } Glib::ustring Gobby::DocumentLocationDialog::get_document_name() const { return m_name_entry->get_text(); } void Gobby::DocumentLocationDialog::set_document_name( const Glib::ustring& document_name) { m_name_entry->set_text(document_name); } InfBrowser* Gobby::DocumentLocationDialog::get_selected_directory( InfBrowserIter* iter) const { GtkTreeIter tree_iter; if(inf_gtk_browser_view_get_selected(m_view, &tree_iter)) { InfGtkBrowserModelStatus status = INF_GTK_BROWSER_MODEL_CONNECTED; GtkTreeIter dummy_iter; if(!gtk_tree_model_iter_parent(GTK_TREE_MODEL(m_filter_model), &dummy_iter, &tree_iter)) { gtk_tree_model_get( GTK_TREE_MODEL(m_filter_model), &tree_iter, INF_GTK_BROWSER_MODEL_COL_STATUS, &status, -1); } if(status == INF_GTK_BROWSER_MODEL_CONNECTED) { InfBrowserIter* browser_iter; InfBrowser* browser; gtk_tree_model_get( GTK_TREE_MODEL(m_filter_model), &tree_iter, INF_GTK_BROWSER_MODEL_COL_BROWSER, &browser, INF_GTK_BROWSER_MODEL_COL_NODE, &browser_iter, -1); *iter = *browser_iter; inf_browser_iter_free(browser_iter); g_object_unref(browser); return browser; } else { return NULL; } } else { return NULL; } } InfGtkBrowserModel* Gobby::DocumentLocationDialog::get_browser_model() const { return INF_GTK_BROWSER_MODEL( gtk_tree_model_filter_get_model( GTK_TREE_MODEL_FILTER(m_filter_model))); } void Gobby::DocumentLocationDialog::set_single_document_mode() { set_title(_("Select document's target location")); m_location_label->set_text( _("Choose a directory to create the document into:")); m_name_label->show(); m_name_entry->show(); } void Gobby::DocumentLocationDialog::set_multiple_document_mode() { set_title(_("Select documents' target location")); m_location_label->set_text( _("Choose a directory to create the documents into:")); m_name_label->hide(); m_name_entry->hide(); } void Gobby::DocumentLocationDialog::on_show() { Gtk::Dialog::on_show(); if(m_name_entry->get_visible()) { m_name_entry->select_region( 0, m_name_entry->get_text_length()); m_name_entry->grab_focus(); } else { gtk_widget_grab_focus(GTK_WIDGET(m_view)); } } void Gobby::DocumentLocationDialog::on_selection_changed(GtkTreeIter* iter) { gboolean accept_sensitive = false; if(iter != NULL) { GtkTreeIter dummy_iter; if(gtk_tree_model_iter_parent(GTK_TREE_MODEL(m_filter_model), &dummy_iter, iter)) { // Child: Always OK accept_sensitive = true; } else { // Top-level node: Check status InfGtkBrowserModelStatus status; gtk_tree_model_get(GTK_TREE_MODEL(m_filter_model), iter, INF_GTK_BROWSER_MODEL_COL_STATUS, &status, -1); accept_sensitive = (status == INF_GTK_BROWSER_MODEL_CONNECTED); } } set_response_sensitive(Gtk::RESPONSE_ACCEPT, accept_sensitive); } void Gobby::DocumentLocationDialog::on_row_changed(GtkTreePath* path, GtkTreeIter* iter) { // If a toplevel entry is selected, and the status changed to // connected, then make the Accept button sensitive. On the other // hand, if status changed to something else, then // make it insensitive. GtkTreeIter sel_iter; if(inf_gtk_browser_view_get_selected(m_view, &sel_iter)) { // We cannot compare the iterators directly here, since // GtkTreeModelFilter seems to allow two iterators pointing to // the same row but having a different user_data3 field. We // therefore compare the paths instead. GtkTreePath* sel_path = gtk_tree_model_get_path( GTK_TREE_MODEL(m_filter_model), &sel_iter); if(gtk_tree_path_compare(path, sel_path) == 0) { GtkTreeIter dummy_iter; if(!gtk_tree_model_iter_parent( GTK_TREE_MODEL(m_filter_model), &dummy_iter, iter)) { InfGtkBrowserModelStatus s; gtk_tree_model_get( GTK_TREE_MODEL(m_filter_model), iter, INF_GTK_BROWSER_MODEL_COL_STATUS, &s, -1); set_response_sensitive( Gtk::RESPONSE_ACCEPT, s == INF_GTK_BROWSER_MODEL_CONNECTED ); } } gtk_tree_path_free(sel_path); } } bool Gobby::DocumentLocationDialog::filter_visible_func(GtkTreeModel* model, GtkTreeIter* iter) { GtkTreeIter dummy_iter; if(!gtk_tree_model_iter_parent(model, &dummy_iter, iter)) { return true; } else { InfBrowserIter* browser_iter; InfBrowser* browser; gtk_tree_model_get( model, iter, INF_GTK_BROWSER_MODEL_COL_BROWSER, &browser, INF_GTK_BROWSER_MODEL_COL_NODE, &browser_iter, -1); bool result = inf_browser_is_subdirectory(browser, browser_iter); inf_browser_iter_free(browser_iter); g_object_unref(browser); return result; } } code/dialogs/document-location-dialog.hpp000066400000000000000000000061011304552132100207760ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_DOCUMENTLOCATIONDIALOG_HPP_ #define _GOBBY_DOCUMENTLOCATIONDIALOG_HPP_ #include #include #include #include #include #include #include namespace Gobby { class DocumentLocationDialog : public Gtk::Dialog { private: friend class Gtk::Builder; DocumentLocationDialog(GtkDialog* cobject, const Glib::RefPtr& builder); public: static std::unique_ptr create( Gtk::Window& parent, InfGtkBrowserModel* model); ~DocumentLocationDialog(); Glib::ustring get_document_name() const; void set_document_name(const Glib::ustring& document_name); InfBrowser* get_selected_directory(InfBrowserIter* iter) const; InfGtkBrowserModel* get_browser_model() const; void set_single_document_mode(); void set_multiple_document_mode(); protected: virtual void on_show(); static void on_selection_changed_static(InfGtkBrowserView* view, GtkTreeIter* iter, gpointer user_data) { static_cast( user_data)->on_selection_changed(iter); } static void on_row_changed_static(GtkTreeModel* model, GtkTreePath* path, GtkTreeIter* iter, gpointer user_data) { static_cast( user_data)->on_row_changed(path, iter); } static gboolean filter_visible_func_static(GtkTreeModel* model, GtkTreeIter* iter, gpointer user_data) { return static_cast( user_data)->filter_visible_func(model, iter); } void on_selection_changed(GtkTreeIter* iter); void on_row_changed(GtkTreePath* path, GtkTreeIter* iter); bool filter_visible_func(GtkTreeModel* model, GtkTreeIter* iter); Gtk::Label* m_name_label; Gtk::Entry* m_name_entry; Gtk::Label* m_location_label; InfGtkBrowserModelFilter* m_filter_model; InfGtkBrowserView* m_view; }; } #endif // _GOBBY_DOCUMENTLOCATIONDIALOG_HPP_ code/dialogs/entry-dialog.cpp000066400000000000000000000042331304552132100165120ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "dialogs/entry-dialog.hpp" Gobby::EntryDialog::EntryDialog(GtkDialog* cobject, const Glib::RefPtr& builder): Gtk::Dialog(cobject) { builder->get_widget("intro-label", m_intro_label); builder->get_widget("entry", m_entry); } std::unique_ptr Gobby::EntryDialog::create(Gtk::Window& parent, const Glib::ustring& title, const Glib::ustring& intro_text) { Glib::RefPtr builder = Gtk::Builder::create_from_resource( "/de/0x539/gobby/ui/entry-dialog.ui"); EntryDialog* dialog = NULL; builder->get_widget_derived("EntryDialog", dialog); dialog->set_transient_for(parent); dialog->set_title(title); dialog->m_intro_label->set_text_with_mnemonic(intro_text); return std::unique_ptr(dialog); } Glib::ustring Gobby::EntryDialog::get_text() const { return m_entry->get_text(); } void Gobby::EntryDialog::set_text(const Glib::ustring& text) { m_entry->set_text(text); } void Gobby::EntryDialog::on_show() { Gtk::Dialog::on_show(); // We can't do this in the constructor, because the buttons are added // by the caller after the widget has been constructed. set_default_response(Gtk::RESPONSE_ACCEPT); m_entry->select_region(0, m_entry->get_text().length()); m_entry->grab_focus(); } code/dialogs/entry-dialog.hpp000066400000000000000000000030151304552132100165140ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_ENTRYDIALOG_HPP_ #define _GOBBY_ENTRYDIALOG_HPP_ #include #include #include #include namespace Gobby { class EntryDialog: public Gtk::Dialog { private: friend class Gtk::Builder; EntryDialog(GtkDialog* cobject, const Glib::RefPtr& builder); public: static std::unique_ptr create( Gtk::Window& parent, const Glib::ustring& title, const Glib::ustring& intro_text); Glib::ustring get_text() const; void set_text(const Glib::ustring& text); protected: virtual void on_show(); Gtk::Label* m_intro_label; Gtk::Entry* m_entry; }; } #endif // _GOBBY_ENTRYDIALOG_HPP_ code/dialogs/find-dialog.cpp000066400000000000000000000302171304552132100162720ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "dialogs/find-dialog.hpp" #include "core/folder.hpp" #include "util/i18n.hpp" #include #include namespace { typedef gboolean (*TextSearchFunc)( const GtkTextIter*, const gchar*, GtkTextSearchFlags, GtkTextIter*, GtkTextIter*, const GtkTextIter* ); const int RESPONSE_FIND = 1; const int RESPONSE_REPLACE = 2; const int RESPONSE_REPLACE_ALL = 3; } Gobby::FindDialog::FindDialog(GtkDialog* cobject, const Glib::RefPtr& builder): Gtk::Dialog(cobject), m_folder(NULL), m_status_bar(NULL) { builder->get_widget("search-for", m_entry_find); builder->get_widget("replace-with-label", m_label_replace); builder->get_widget("replace-with", m_entry_replace); builder->get_widget("match-case", m_check_case); builder->get_widget("match-entire-word-only", m_check_whole_word); builder->get_widget("search-backwards", m_check_backwards); builder->get_widget("wrap-around", m_check_wrap_around); m_entry_find->signal_changed().connect( sigc::mem_fun(*this, &FindDialog::on_find_text_changed)); m_entry_replace->signal_changed().connect( sigc::mem_fun(*this, &FindDialog::on_replace_text_changed)); add_button(_("_Close"), Gtk::RESPONSE_CLOSE); m_button_replace_all = add_button(_("Replace _All"), RESPONSE_REPLACE_ALL); m_button_replace = add_button(_("_Replace"), RESPONSE_REPLACE); add_button(_("_Find"), RESPONSE_FIND); set_default_response(RESPONSE_FIND); } std::unique_ptr Gobby::FindDialog::create(Gtk::Window& parent, const Folder& folder, StatusBar& status_bar) { Glib::RefPtr builder = Gtk::Builder::create_from_resource( "/de/0x539/gobby/ui/find-dialog.ui"); FindDialog* dialog_ptr; builder->get_widget_derived("FindDialog", dialog_ptr); std::unique_ptr dialog(dialog_ptr); dialog->set_transient_for(parent); dialog->m_folder = &folder; dialog->m_status_bar = &status_bar; folder.signal_document_changed().connect( sigc::mem_fun(*dialog, &FindDialog::on_document_changed)); // For initial sensitivity: dialog->on_document_changed(folder.get_current_document()); return dialog; } Gobby::FindDialog::~FindDialog() { on_document_changed(NULL); } bool Gobby::FindDialog::get_search_only() const { return m_label_replace->get_visible(); } void Gobby::FindDialog::set_search_only(bool search_only) { if(search_only) { m_label_replace->hide(); m_entry_replace->hide(); m_button_replace->hide(); m_button_replace_all->hide(); } else { m_label_replace->show(); m_entry_replace->show(); m_button_replace->show(); m_button_replace_all->show(); } set_title(search_only ? _("Find") : _("Replace") ); } Glib::ustring Gobby::FindDialog::get_find_text() const { return m_entry_find->get_text(); } Glib::ustring Gobby::FindDialog::get_replace_text() const { return m_entry_replace->get_text(); } bool Gobby::FindDialog::find_next() { bool result = find_and_select(NULL, SEARCH_FORWARD); if(!result) { Glib::ustring str = Glib::ustring::compose( _("Phrase \"%1\" has not been found"), get_find_text()); m_status_bar->add_info_message(str, 5); return false; } return true; } bool Gobby::FindDialog::find_previous() { bool result = find_and_select(NULL, SEARCH_BACKWARD); if(!result) { Glib::ustring str = Glib::ustring::compose( _("Phrase \"%1\" has not been found"), get_find_text()); m_status_bar->add_info_message(str, 5); return false; } return true; } void Gobby::FindDialog::on_show() { Gtk::Dialog::on_show(); m_entry_find->grab_focus(); } void Gobby::FindDialog::on_response(int id) { switch(id) { case Gtk::RESPONSE_CLOSE: hide(); break; case RESPONSE_FIND: find(); break; case RESPONSE_REPLACE: replace(); break; case RESPONSE_REPLACE_ALL: replace_all(); break; } Gtk::Dialog::on_response(id); } void Gobby::FindDialog::on_document_changed(SessionView* view) { m_active_user_changed_connection.disconnect(); TextSessionView* text_view = dynamic_cast(view); if(text_view != NULL) { m_active_user_changed_connection = text_view->signal_active_user_changed().connect( sigc::mem_fun( *this, &FindDialog::on_active_user_changed)); } update_sensitivity(); } void Gobby::FindDialog::on_active_user_changed(InfUser* user) { update_sensitivity(); } void Gobby::FindDialog::on_find_text_changed() { update_sensitivity(); m_signal_find_text_changed.emit(); } void Gobby::FindDialog::on_replace_text_changed() { m_signal_replace_text_changed.emit(); } Gobby::FindDialog::SearchDirection Gobby::FindDialog::get_direction() const { if(m_check_backwards->get_active()) return SEARCH_BACKWARD; else return SEARCH_FORWARD; } bool Gobby::FindDialog::find() { if(get_direction() == SEARCH_FORWARD) return find_next(); else return find_previous(); } bool Gobby::FindDialog::replace() { SessionView* view = m_folder->get_current_document(); TextSessionView* text_view = dynamic_cast(view); g_assert(text_view != NULL); // Get selected string Glib::ustring sel_str = text_view->get_selected_text(); Glib::ustring find_str = get_find_text(); // Lowercase both if we are comparing insensitive if(!m_check_case->get_active() ) { sel_str = sel_str.casefold(); find_str = find_str.casefold(); } // Replace them if they are the same if(sel_str == find_str) { GtkTextBuffer* buffer = GTK_TEXT_BUFFER(text_view->get_text_buffer()); // Replace occurrence Glib::ustring replace_text = get_replace_text(); gtk_text_buffer_delete_selection(buffer, TRUE, TRUE); gtk_text_buffer_insert_at_cursor(buffer, replace_text.c_str(), replace_text.bytes()); // and find the next find_and_select(NULL, get_direction()); return true; } else { // Search the first occurrence return find(); } } bool Gobby::FindDialog::replace_all() { // TODO: Add helper function to get textsessionview? Maybe even add // to Folder? SessionView* view = m_folder->get_current_document(); TextSessionView* text_view = dynamic_cast(view); g_assert(text_view != NULL); GtkTextIter begin; GtkTextBuffer* buffer = GTK_TEXT_BUFFER(text_view->get_text_buffer()); gtk_text_buffer_get_start_iter(buffer, &begin); unsigned int replace_count = 0; GtkTextIter match_start, match_end; while(find_range(&begin, NULL, SEARCH_FORWARD, &match_start, &match_end)) { Glib::ustring replace_text = get_replace_text(); gtk_text_buffer_delete(buffer, &match_start, &match_end); gtk_text_buffer_insert(buffer, &match_start, replace_text.c_str(), replace_text.bytes()); ++ replace_count; begin = match_start; } Glib::ustring message; bool result; if(replace_count == 0) { message = _("No occurrence has been replaced"); result = false; } else { message = Glib::ustring::compose( ngettext("%1 occurrence has been replaced", "%1 occurrences have been replaced", replace_count), replace_count); result = true; } m_status_bar->add_info_message(message, 5); return result; } bool Gobby::FindDialog::find_and_select(const GtkTextIter* from, SearchDirection direction) { SessionView* view = m_folder->get_current_document(); TextSessionView* text_view = dynamic_cast(view); g_assert(text_view != NULL); const GtkTextIter* real_begin = from; GtkTextIter insert_iter; // Search from cursor position if from is not given if(from == NULL) { GtkTextBuffer* buffer = GTK_TEXT_BUFFER(text_view->get_text_buffer()); GtkTextMark* mark = gtk_text_buffer_get_insert(buffer); gtk_text_buffer_get_iter_at_mark(buffer, &insert_iter, mark); real_begin = &insert_iter; } GtkTextIter match_start, match_end; if(find_wrap(real_begin, direction, &match_start, &match_end)) { if(direction == SEARCH_FORWARD) text_view->set_selection(&match_end, &match_start); else text_view->set_selection(&match_start, &match_end); return true; } return false; } bool Gobby::FindDialog::find_wrap(const GtkTextIter* from, SearchDirection direction, GtkTextIter* match_start, GtkTextIter* match_end) { SessionView* view = m_folder->get_current_document(); TextSessionView* text_view = dynamic_cast(view); g_assert(text_view != NULL); GtkTextIter start_pos = *from; bool result = find_range(&start_pos, NULL, direction, match_start, match_end); if(result == true) return true; if(!m_check_wrap_around->get_active()) return false; // Wrap around GtkTextIter restart_pos; GtkTextBuffer* buffer = GTK_TEXT_BUFFER(text_view->get_text_buffer()); if(direction == SEARCH_FORWARD) gtk_text_buffer_get_start_iter(buffer, &restart_pos); else gtk_text_buffer_get_end_iter(buffer, &restart_pos); // Limit to search to: Normally the position where we started. GtkTextIter* relimit = &start_pos; if(direction == SEARCH_BACKWARD) { // ??? gtk_text_iter_forward_chars(&start_pos, get_find_text().length()); if(gtk_text_iter_is_end(&start_pos)) relimit = NULL; } return find_range(&restart_pos, relimit, direction, match_start, match_end); } bool Gobby::FindDialog::find_range(const GtkTextIter* from, const GtkTextIter* to, SearchDirection direction, GtkTextIter* match_start, GtkTextIter* match_end) { GtkTextIter start_pos = *from; while(find_range_once(&start_pos, to, direction, match_start, match_end)) { if(m_check_whole_word->get_active() ) { if(!gtk_text_iter_starts_word(match_start) || !gtk_text_iter_ends_word(match_end)) { if(direction == SEARCH_FORWARD) start_pos = *match_end; else start_pos = *match_start; continue; } } return true; } return false; } bool Gobby::FindDialog::find_range_once(const GtkTextIter* from, const GtkTextIter* to, SearchDirection direction, GtkTextIter* match_start, GtkTextIter* match_end) { GtkTextSearchFlags flags = GtkTextSearchFlags(0); if(!m_check_case->get_active() ) flags = GTK_TEXT_SEARCH_CASE_INSENSITIVE; TextSearchFunc search_func = (direction == SEARCH_FORWARD ? gtk_text_iter_forward_search : gtk_text_iter_backward_search); Glib::ustring find_str = m_entry_find->get_text(); gboolean result = search_func( from, find_str.c_str(), flags, match_start, match_end, to != NULL ? to : NULL ); return result; } void Gobby::FindDialog::update_sensitivity() { SessionView* view = m_folder->get_current_document(); TextSessionView* text_view = dynamic_cast(view); bool find_sensitivity = (!m_entry_find->get_text().empty() && text_view != NULL); bool replace_sensitivity = (find_sensitivity && text_view->get_active_user() != NULL); set_response_sensitive(RESPONSE_FIND, find_sensitivity); set_response_sensitive(RESPONSE_REPLACE, replace_sensitivity); set_response_sensitive(RESPONSE_REPLACE_ALL, replace_sensitivity); } code/dialogs/find-dialog.hpp000066400000000000000000000077431304552132100163070ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_FINDDIALOG_HPP_ #define _GOBBY_FINDDIALOG_HPP_ #include "core/folder.hpp" #include "core/statusbar.hpp" #include "core/sessionview.hpp" #include #include #include #include #include namespace Gobby { class FindDialog: public Gtk::Dialog { private: friend class Gtk::Builder; FindDialog(GtkDialog* cobject, const Glib::RefPtr& builder); public: typedef sigc::signal SignalFindTextChanged; typedef sigc::signal SignalReplaceTextChanged; static std::unique_ptr create(Gtk::Window& parent, const Folder& folder, StatusBar& status_bar); ~FindDialog(); bool get_search_only() const; void set_search_only(bool search_only); Glib::ustring get_find_text() const; Glib::ustring get_replace_text() const; bool find_next(); bool find_previous(); SignalFindTextChanged signal_find_text_changed() const { return m_signal_find_text_changed; } SignalReplaceTextChanged signal_replace_text_changed() const { return m_signal_replace_text_changed; } protected: enum SearchDirection { SEARCH_FORWARD, SEARCH_BACKWARD }; virtual void on_show(); virtual void on_response(int id); void on_document_changed(SessionView* view); void on_active_user_changed(InfUser* user); void on_find_text_changed(); void on_replace_text_changed(); SearchDirection get_direction() const; bool find(); bool replace(); bool replace_all(); // Searches for an occurence with the provided options, selecting the // result, if any. bool find_and_select(const GtkTextIter* from, SearchDirection direction); // Searches for a occurence, beginning from from, and wrapping around // if the corresponding checkbutton is checked. bool find_wrap(const GtkTextIter* from, SearchDirection direction, GtkTextIter* match_start, GtkTextIter* match_end); // Searches for an occurence in the given range bool find_range(const GtkTextIter* from, const GtkTextIter* to, SearchDirection direction, GtkTextIter* match_start, GtkTextIter* match_end); // Searches for an occurence, ignoring whole word only option bool find_range_once(const GtkTextIter* from, const GtkTextIter* to, SearchDirection direction, GtkTextIter* match_start, GtkTextIter* match_end); const Folder* m_folder; StatusBar* m_status_bar; sigc::connection m_active_user_changed_connection; Gtk::Label* m_label_replace; Gtk::Entry* m_entry_find; Gtk::Entry* m_entry_replace; Gtk::CheckButton* m_check_case; Gtk::CheckButton* m_check_whole_word; Gtk::CheckButton* m_check_backwards; Gtk::CheckButton* m_check_wrap_around; Gtk::Button* m_button_replace; Gtk::Button* m_button_replace_all; SignalFindTextChanged m_signal_find_text_changed; SignalReplaceTextChanged m_signal_replace_text_changed; private: void update_sensitivity(); }; } #endif // _GOBBY_FINDDIALOG_HPP_ code/dialogs/goto-dialog.cpp000066400000000000000000000075351304552132100163310ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "dialogs/goto-dialog.hpp" #include "util/i18n.hpp" #include #include Gobby::GotoDialog::GotoDialog(GtkDialog* cobject, const Glib::RefPtr& builder): Gtk::Dialog(cobject), m_folder(NULL), m_current_view(NULL) { builder->get_widget("spin-button", m_entry_line); m_entry_line->set_increments(1, 10); add_button(_("_Close"), Gtk::RESPONSE_CLOSE); add_button(_("Go To _Line"), Gtk::RESPONSE_ACCEPT); set_default_response(Gtk::RESPONSE_ACCEPT); } Gobby::GotoDialog::~GotoDialog() { on_document_changed(NULL); } std::unique_ptr Gobby::GotoDialog::create(Gtk::Window& parent, const Folder& folder) { Glib::RefPtr builder = Gtk::Builder::create_from_resource( "/de/0x539/gobby/ui/goto-dialog.ui"); GotoDialog* dialog_ptr; builder->get_widget_derived("GotoDialog", dialog_ptr); std::unique_ptr dialog(dialog_ptr); dialog->set_transient_for(parent); dialog->m_folder = &folder; folder.signal_document_changed().connect( sigc::mem_fun(*dialog, &GotoDialog::on_document_changed)); // For initial sensitivity: dialog->on_document_changed(folder.get_current_document()); return dialog; } void Gobby::GotoDialog::on_show() { Gtk::Dialog::on_show(); m_entry_line->grab_focus(); if(m_current_view != NULL) { GtkTextBuffer* buffer = GTK_TEXT_BUFFER( m_current_view->get_text_buffer()); GtkTextIter cursor; gtk_text_buffer_get_iter_at_mark( buffer, &cursor, gtk_text_buffer_get_insert(buffer)); m_entry_line->set_value(gtk_text_iter_get_line(&cursor) + 1); m_entry_line->select_region( 0, m_entry_line->get_text().length()); } } void Gobby::GotoDialog::on_response(int id) { if(id == Gtk::RESPONSE_ACCEPT) { g_assert(m_current_view != NULL); int value = m_entry_line->get_value_as_int(); GtkTextBuffer* buffer = GTK_TEXT_BUFFER( m_current_view->get_text_buffer()); GtkTextIter begin; gtk_text_buffer_get_iter_at_line(buffer, &begin, value - 1); m_current_view->set_selection(&begin, &begin); } else if(id == Gtk::RESPONSE_CLOSE) { hide(); } Gtk::Dialog::on_response(id); } void Gobby::GotoDialog::on_document_changed(SessionView* view) { if(m_current_view != NULL) { GtkTextBuffer* buffer = GTK_TEXT_BUFFER( m_current_view->get_text_buffer()); g_signal_handler_disconnect(buffer, m_changed_handler); } m_current_view = dynamic_cast(view); set_response_sensitive(Gtk::RESPONSE_ACCEPT, m_current_view != NULL); m_entry_line->set_sensitive(m_current_view != NULL); if(m_current_view != NULL) { GtkTextBuffer* buffer = GTK_TEXT_BUFFER( m_current_view->get_text_buffer()); m_changed_handler = g_signal_connect_after( G_OBJECT(buffer), "changed", G_CALLBACK(on_changed_static), this); on_changed(); } } void Gobby::GotoDialog::on_changed() { g_assert(m_current_view != NULL); GtkTextBuffer* buffer = GTK_TEXT_BUFFER( m_current_view->get_text_buffer()); m_entry_line->set_range(1, gtk_text_buffer_get_line_count(buffer)); } code/dialogs/goto-dialog.hpp000066400000000000000000000034761304552132100163360ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_GOTODIALOG_HPP_ #define _GOBBY_GOTODIALOG_HPP_ #include "core/folder.hpp" #include "core/sessionview.hpp" #include #include #include namespace Gobby { class GotoDialog: public Gtk::Dialog { private: friend class Gtk::Builder; GotoDialog(GtkDialog* cobject, const Glib::RefPtr& builder); public: ~GotoDialog(); static std::unique_ptr create(Gtk::Window& parent, const Folder& folder); protected: static void on_changed_static(GtkTextBuffer* buffer, gpointer user_data) { static_cast(user_data)->on_changed(); } virtual void on_show(); virtual void on_response(int id); void on_document_changed(SessionView* view); void on_changed(); const Folder* m_folder; Gtk::SpinButton* m_entry_line; TextSessionView* m_current_view; gulong m_changed_handler; }; } #endif // _GOBBY_GOTODIALOG_HPP_ code/dialogs/initial-dialog.cpp000066400000000000000000000217411304552132100170050ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "dialogs/initial-dialog.hpp" #include "core/credentialsgenerator.hpp" #include "util/file.hpp" #include "util/i18n.hpp" #include "features.hpp" #include #include namespace { Gtk::Widget* align_top(Gtk::Widget& widget) { Gtk::Alignment* alignment = new Gtk::Alignment(Gtk::ALIGN_CENTER, Gtk::ALIGN_START, 1.0, 0.0); alignment->add(widget); alignment->show(); return Gtk::manage(alignment); } Gtk::Widget* indent(Gtk::Widget& widget) { Gtk::Alignment* alignment = new Gtk::Alignment; alignment->set_padding(0, 0, 12, 0); alignment->add(widget); alignment->show(); return Gtk::manage(alignment); } class InitialCertGenerator { public: InitialCertGenerator(Gobby::CertificateManager& cert_manager, Gobby::StatusBar& status_bar, const std::string& key_filename, const std::string& cert_filename): m_cert_manager(cert_manager), m_status_bar(status_bar), m_key_filename(key_filename), m_cert_filename(cert_filename), m_key(NULL), m_status_handle(m_status_bar.invalid_handle()) { m_key_handle = Gobby::create_key( GNUTLS_PK_RSA, 2048, sigc::mem_fun( *this, &InitialCertGenerator:: on_key_generated)); m_status_handle = m_status_bar.add_info_message( Gobby::_("Generating 2048-bit " "RSA private key...")); } ~InitialCertGenerator() { if(m_key != NULL) gnutls_x509_privkey_deinit(m_key); if(m_status_handle != m_status_bar.invalid_handle()) m_status_bar.remove_message(m_status_handle); } private: void on_key_generated(const Gobby::KeyGeneratorHandle* handle, gnutls_x509_privkey_t key, const GError* error) { m_key_handle.reset(NULL); m_key = key; if(error != NULL) { m_status_bar.add_error_message( Gobby::_("Failed to generate " "private key"), Glib::ustring::compose( Gobby::_("%1\n\nYou can try " "again to create a " "key in the " "Security tab of " "the preferences " "dialog."), error->message)); m_cert_manager.set_private_key( NULL, m_key_filename.c_str(), error); done(); return; } m_crt_handle = Gobby::create_self_signed_certificate( m_key, sigc::mem_fun( *this, &InitialCertGenerator:: on_cert_generated)); } void on_cert_generated( const Gobby::CertificateGeneratorHandle* hndl, gnutls_x509_crt_t cert, const GError* error) { m_status_bar.remove_message(m_status_handle); m_status_handle = m_status_bar.invalid_handle(); m_crt_handle.reset(NULL); m_cert_manager.set_private_key( m_key, m_key_filename.c_str(), NULL); m_key = NULL; if(error != NULL) { m_status_bar.add_error_message( Gobby::_("Failed to generate " "self-signed certificate"), Glib::ustring::compose( Gobby::_("%1\n\nYou can try " "again to create a " "certificate in the " "Security tab of " "the preferences " "dialog."), error->message)); } else { // Note this needs to be allocated with // g_malloc, since it is directly passed and // freed by libinfinity gnutls_x509_crt_t* crt_array = static_cast( g_malloc(sizeof( gnutls_x509_crt_t))); crt_array[0] = cert; m_cert_manager.set_certificates( crt_array, 1, m_cert_filename.c_str(), NULL); } done(); } void done() { delete this; } Gobby::CertificateManager& m_cert_manager; Gobby::StatusBar& m_status_bar; const std::string m_key_filename; const std::string m_cert_filename; gnutls_x509_privkey_t m_key; std::unique_ptr m_key_handle; std::unique_ptr m_crt_handle; Gobby::StatusBar::MessageHandle m_status_handle; }; } Gobby::InitialDialog::InitialDialog( GtkDialog* cobject, const Glib::RefPtr& builder) : Gtk::Dialog(cobject), m_status_bar(NULL), m_preferences(NULL), m_cert_manager(NULL) { builder->get_widget("name-entry", m_name_entry); builder->get_widget_derived("color-button", m_color_button); builder->get_widget("allow-remote-connections", m_remote_allow_connections); builder->get_widget("ask-password", m_remote_require_password); builder->get_widget("password", m_remote_password_entry); builder->get_widget("create-self-signed", m_remote_auth_self); builder->get_widget("use-existing-certificate", m_remote_auth_external); builder->get_widget("private-key-file", m_remote_auth_external_keyfile); builder->get_widget("certificate-file", m_remote_auth_external_certfile); builder->get_widget("remote-connections-grid", m_remote_connections_grid); builder->get_widget("password-grid", m_password_grid); builder->get_widget("certificate-grid", m_certificate_grid); m_remote_allow_connections->signal_toggled().connect( sigc::mem_fun(*this, &Gobby::InitialDialog:: on_remote_allow_connections_toggled)); m_remote_require_password->signal_toggled().connect( sigc::mem_fun(*this, &Gobby::InitialDialog:: on_remote_require_password_toggled)); m_remote_auth_external->signal_toggled().connect( sigc::mem_fun(*this, &Gobby::InitialDialog:: on_remote_auth_external_toggled)); add_button(_("_Close"), Gtk::RESPONSE_CLOSE); } std::unique_ptr Gobby::InitialDialog::create(Gtk::Window& parent, StatusBar& status_bar, Preferences& preferences, CertificateManager& cert_manager) { Glib::RefPtr builder = Gtk::Builder::create_from_resource( "/de/0x539/gobby/ui/initial-dialog.ui"); InitialDialog* dialog_ptr; builder->get_widget_derived("InitialDialog", dialog_ptr); std::unique_ptr dialog(dialog_ptr); dialog->set_transient_for(parent); dialog->m_status_bar = &status_bar; dialog->m_preferences = &preferences; dialog->m_cert_manager = &cert_manager; // Set initial values dialog->m_name_entry->set_text(preferences.user.name); dialog->m_color_button->set_hue(preferences.user.hue); dialog->m_remote_allow_connections->set_active( preferences.user.allow_remote_access); dialog->m_remote_require_password->set_active( preferences.user.require_password); dialog->m_remote_password_entry->set_text( static_cast(preferences.user.password)); // Set initial sensitivity dialog->on_remote_allow_connections_toggled(); dialog->on_remote_require_password_toggled(); dialog->on_remote_auth_external_toggled(); return dialog; } void Gobby::InitialDialog::on_response(int id) { m_preferences->user.name = m_name_entry->get_text(); m_preferences->user.hue = m_color_button->get_hue(); m_preferences->user.allow_remote_access = m_remote_allow_connections->get_active(); m_preferences->user.require_password = m_remote_require_password->get_active(); m_preferences->user.password = m_remote_password_entry->get_text(); m_preferences->security.authentication_enabled = true; if(m_remote_auth_self->get_active()) { // The certificate generator takes care of its own: new InitialCertGenerator( *m_cert_manager, *m_status_bar, config_filename("key.pem"), config_filename("cert.pem")); } else { m_preferences->security.certificate_file = m_remote_auth_external_certfile->get_filename(); m_preferences->security.key_file = m_remote_auth_external_keyfile->get_filename(); } hide(); } void Gobby::InitialDialog::on_remote_allow_connections_toggled() { m_remote_connections_grid->set_sensitive( m_remote_allow_connections->get_active()); } void Gobby::InitialDialog::on_remote_require_password_toggled() { m_password_grid->set_sensitive( m_remote_require_password->get_active()); } void Gobby::InitialDialog::on_remote_auth_external_toggled() { m_certificate_grid->set_sensitive( m_remote_auth_external->get_active()); } code/dialogs/initial-dialog.hpp000066400000000000000000000045201304552132100170060ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_INITIALDIALOG_HPP_ #define _GOBBY_INITIALDIALOG_HPP_ #include "core/certificatemanager.hpp" #include "core/statusbar.hpp" #include "core/preferences.hpp" #include "core/huebutton.hpp" #include #include #include #include #include #include namespace Gobby { class InitialDialog : public Gtk::Dialog { private: friend class Gtk::Builder; InitialDialog(GtkDialog* cobject, const Glib::RefPtr& builder); public: static std::unique_ptr create(Gtk::Window& parent, StatusBar& status_bar, Preferences& preferences, CertificateManager& cert_manager); protected: virtual void on_response(int id); void on_remote_allow_connections_toggled(); void on_remote_require_password_toggled(); void on_remote_auth_external_toggled(); StatusBar* m_status_bar; Preferences* m_preferences; CertificateManager* m_cert_manager; Gtk::Entry* m_name_entry; HueButton* m_color_button; Gtk::CheckButton* m_remote_allow_connections; Gtk::CheckButton* m_remote_require_password; Gtk::Entry* m_remote_password_entry; Gtk::RadioButton* m_remote_auth_self; Gtk::RadioButton* m_remote_auth_external; Gtk::FileChooserButton* m_remote_auth_external_keyfile; Gtk::FileChooserButton* m_remote_auth_external_certfile; Gtk::Grid* m_remote_connections_grid; Gtk::Grid* m_password_grid; Gtk::Grid* m_certificate_grid; }; } #endif // _GOBBY_INITIALDIALOG_HPP_ code/dialogs/open-location-dialog.cpp000066400000000000000000000051361304552132100201230ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "dialogs/open-location-dialog.hpp" #include "util/file.hpp" #include "util/i18n.hpp" #include "features.hpp" Gobby::OpenLocationDialog::OpenLocationDialog( GtkDialog* cobject, const Glib::RefPtr& builder) : Gtk::Dialog(cobject), m_combo(builder, "location-combo", config_filename("recent_uris"), 8) { m_combo.get_entry()->set_activates_default(true); m_combo.get_entry()->signal_changed().connect( sigc::mem_fun(*this, &OpenLocationDialog::on_entry_changed)); } std::unique_ptr Gobby::OpenLocationDialog::create(Gtk::Window& parent) { Glib::RefPtr builder = Gtk::Builder::create_from_resource( "/de/0x539/gobby/ui/open-location-dialog.ui"); OpenLocationDialog* dialog; builder->get_widget_derived("OpenLocationDialog", dialog); dialog->set_transient_for(parent); return std::unique_ptr(dialog); } Glib::ustring Gobby::OpenLocationDialog::get_uri() const { return m_combo.get_entry()->get_text(); } void Gobby::OpenLocationDialog::set_uri(const Glib::ustring& uri) { m_combo.get_entry()->set_text(uri); } void Gobby::OpenLocationDialog::on_response(int response_id) { if(response_id == Gtk::RESPONSE_ACCEPT) m_combo.commit(); Gtk::Dialog::on_response(response_id); } void Gobby::OpenLocationDialog::on_show() { Gtk::Dialog::on_show(); // We can't do this in the constructor, because the buttons are added // by the caller after the widget has been constructed. set_default_response(Gtk::RESPONSE_ACCEPT); on_entry_changed(); m_combo.get_entry()->select_region( 0, m_combo.get_entry()->get_text().length()); m_combo.grab_focus(); } void Gobby::OpenLocationDialog::on_entry_changed() { set_response_sensitive( Gtk::RESPONSE_ACCEPT, !m_combo.get_entry()->get_text().empty()); } code/dialogs/open-location-dialog.hpp000066400000000000000000000030271304552132100201250ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_OPENLOCATIONDIALOG_HPP_ #define _GOBBY_OPENLOCATIONDIALOG_HPP_ #include #include #include "util/historyentry.hpp" namespace Gobby { class OpenLocationDialog: public Gtk::Dialog { private: friend class Gtk::Builder; OpenLocationDialog(GtkDialog* cobject, const Glib::RefPtr& builder); public: static std::unique_ptr create(Gtk::Window& parent); Glib::ustring get_uri() const; void set_uri(const Glib::ustring& uri); protected: virtual void on_response(int response_id); virtual void on_show(); void on_entry_changed(); HistoryComboBox m_combo; }; } #endif // _GOBBY_OPENLOCATIONDIALOG_HPP_ code/dialogs/password-dialog.cpp000066400000000000000000000046441304552132100172210ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "dialogs/password-dialog.hpp" #include "util/i18n.hpp" Gobby::PasswordDialog::PasswordDialog( const Glib::RefPtr& builder, const Glib::ustring& remote_id, unsigned int retry_counter) : Gtk::Dialog(GTK_DIALOG(gtk_builder_get_object( builder->gobj(), "PasswordDialog"))) { Gtk::Label* intro_label; builder->get_widget("intro-label", intro_label); builder->get_widget("password", m_entry); if(retry_counter == 0) { intro_label->set_text(Glib::ustring::compose( _("Connection to host \"%1\" requires a password."), remote_id)); } else { intro_label->set_text(Glib::ustring::compose( _("Invalid password for host \"%1\". " "Please try again."), remote_id)); } } std::unique_ptr Gobby::PasswordDialog::create(Gtk::Window& parent, const Glib::ustring& remote_id, unsigned int retry_counter) { Glib::RefPtr builder = Gtk::Builder::create_from_resource( "/de/0x539/gobby/ui/password-dialog.ui"); std::unique_ptr dialog( new PasswordDialog(builder, remote_id, retry_counter)); dialog->set_transient_for(parent); return dialog; } Glib::ustring Gobby::PasswordDialog::get_password() const { return m_entry->get_text(); } void Gobby::PasswordDialog::on_show() { Gtk::Dialog::on_show(); // We can't do this in the constructor, because the buttons are added // by the caller after the widget has been constructed. set_default_response(Gtk::RESPONSE_ACCEPT); m_entry->select_region(0, m_entry->get_text().length()); m_entry->grab_focus(); } code/dialogs/password-dialog.hpp000066400000000000000000000027401304552132100172210ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_PASSWORDDIALOG_HPP_ #define _GOBBY_PASSWORDDIALOG_HPP_ #include #include #include namespace Gobby { class PasswordDialog: public Gtk::Dialog { private: PasswordDialog(const Glib::RefPtr& builder, const Glib::ustring& remote_id, unsigned int retry_counter); public: static std::unique_ptr create( Gtk::Window& parent, const Glib::ustring& remote_id, unsigned int retry_counter); Glib::ustring get_password() const; protected: virtual void on_show(); Gtk::Entry* m_entry; }; } #endif // _GOBBY_PASSWORDDIALOG_HPP_ code/dialogs/preferences-dialog.cpp000066400000000000000000000647221304552132100176630ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "dialogs/preferences-dialog.hpp" #include "util/i18n.hpp" #include #include #include #include #include #include #include namespace { using namespace Gobby; Gtk::WrapMode wrap_mode_from_check_buttons(Gtk::CheckButton& char_button, Gtk::CheckButton& word_button) { if(!char_button.get_active()) return Gtk::WRAP_NONE; else if(!word_button.get_active()) return Gtk::WRAP_CHAR; else return Gtk::WRAP_WORD_CHAR; } template void connect_option(Gtk::CheckButton& checkbutton, Preferences::Option& option) { checkbutton.signal_toggled().connect( sigc::compose( sigc::mem_fun( option, &Preferences::Option::set ), sigc::mem_fun( checkbutton, &Gtk::CheckButton::get_active ) ) ); } template void connect_option(Gtk::Scale& scale, Preferences::Option& option) { scale.signal_value_changed().connect( sigc::compose( sigc::mem_fun( option, &Preferences::Option::set ), sigc::mem_fun( scale, &Gtk::Scale::get_value ) ) ); } template void connect_option(Gtk::SpinButton& spinbutton, Preferences::Option& option) { spinbutton.signal_value_changed().connect( sigc::compose( sigc::mem_fun( option, &Preferences::Option::set ), sigc::mem_fun( spinbutton, &Gtk::SpinButton::get_value ) ) ); } template void connect_option(Gtk::Entry& entry, Preferences::Option& option) { entry.signal_changed().connect( sigc::compose( sigc::mem_fun( option, &Preferences::Option::set ), sigc::mem_fun( entry, &Gtk::Entry::get_text ) ) ); } void connect_wrap_option(Gtk::CheckButton& char_button, Gtk::CheckButton& word_button, Preferences::Option& option) { sigc::slot toggled_slot( sigc::compose( sigc::mem_fun( option, &Preferences::Option< Gtk::WrapMode>::set ), sigc::bind( sigc::ptr_fun( wrap_mode_from_check_buttons), sigc::ref(char_button), sigc::ref(word_button) ) ) ); char_button.signal_toggled().connect(toggled_slot); word_button.signal_toggled().connect(toggled_slot); } void connect_hue_option(Gobby::HueButton& hue_button, Preferences::Option& option) { hue_button.property_color().signal_changed().connect( sigc::compose( sigc::mem_fun( option, &Preferences::Option::set ), sigc::mem_fun( hue_button, &Gobby::HueButton::get_hue ) ) ); } } Gobby::PreferencesDialog::User::User( const Glib::RefPtr& builder, Preferences& preferences) { builder->get_widget("user-name", m_ent_user_name); builder->get_widget_derived("user-color", m_btn_user_color); builder->get_widget("color-intensity", m_scl_user_alpha); builder->get_widget("remote-show-cursors", m_btn_remote_show_cursors); builder->get_widget("remote-show-selections", m_btn_remote_show_selections); builder->get_widget("remote-show-current-line", m_btn_remote_show_current_lines); builder->get_widget("remote-show-in-scrollbar", m_btn_remote_show_cursor_positions); builder->get_widget("grid-local-connections", m_grid_local_connections); builder->get_widget("remote-allow-edit", m_btn_local_allow_connections); builder->get_widget("require-password", m_btn_local_require_password); builder->get_widget("grid-password", m_grid_password); builder->get_widget("password-entry", m_ent_local_password); builder->get_widget("port-number", m_ent_local_port); builder->get_widget("remember-local-documents", m_btn_local_keep_documents); builder->get_widget("grid-local-documents-directory", m_grid_local_documents_directory); builder->get_widget("local-documents-directory", m_btn_local_documents_directory); m_conn_local_documents_directory.reset(new PathConnection( *m_btn_local_documents_directory, preferences.user.host_directory)); m_btn_local_allow_connections->signal_toggled().connect( sigc::mem_fun( *this, &User::on_local_allow_connections_toggled)); m_btn_local_require_password->signal_toggled().connect( sigc::mem_fun( *this, &User::on_local_require_password_toggled)); m_btn_local_keep_documents->signal_toggled().connect( sigc::mem_fun( *this, &User::on_local_keep_documents_toggled)); m_ent_user_name->set_text(preferences.user.name); connect_option(*m_ent_user_name, preferences.user.name); m_btn_user_color->set_hue(preferences.user.hue); m_btn_user_color->set_saturation(0.35); m_btn_user_color->set_value(1.0); connect_hue_option(*m_btn_user_color, preferences.user.hue); m_scl_user_alpha->set_range(0.0, 1.0); m_scl_user_alpha->set_increments(0.0025, 0.1); m_scl_user_alpha->set_value(preferences.user.alpha); connect_option(*m_scl_user_alpha, preferences.user.alpha); m_btn_remote_show_cursors->set_active( preferences.user.show_remote_cursors); connect_option(*m_btn_remote_show_cursors, preferences.user.show_remote_cursors); m_btn_remote_show_selections->set_active( preferences.user.show_remote_selections); connect_option(*m_btn_remote_show_selections, preferences.user.show_remote_selections); m_btn_remote_show_current_lines->set_active( preferences.user.show_remote_current_lines); connect_option(*m_btn_remote_show_current_lines, preferences.user.show_remote_current_lines); m_btn_remote_show_cursor_positions->set_active( preferences.user.show_remote_cursor_positions); connect_option(*m_btn_remote_show_cursor_positions, preferences.user.show_remote_cursor_positions); m_btn_local_allow_connections->set_active( preferences.user.allow_remote_access); connect_option(*m_btn_local_allow_connections, preferences.user.allow_remote_access); m_btn_local_require_password->set_active( preferences.user.require_password); connect_option(*m_btn_local_require_password, preferences.user.require_password); m_ent_local_password->set_text( static_cast(preferences.user.password)); connect_option(*m_ent_local_password, preferences.user.password); m_ent_local_port->set_range(1, 65535); m_ent_local_port->set_value(preferences.user.port); m_ent_local_port->set_increments(1, 1); connect_option(*m_ent_local_port, preferences.user.port); m_btn_local_keep_documents->set_active( preferences.user.keep_local_documents); connect_option(*m_btn_local_keep_documents, preferences.user.keep_local_documents); m_btn_local_documents_directory->set_filename( static_cast(preferences.user.host_directory)); // Initial sensitivity on_local_allow_connections_toggled(); on_local_require_password_toggled(); on_local_keep_documents_toggled(); } void Gobby::PreferencesDialog::User::on_local_allow_connections_toggled() { m_grid_local_connections->set_sensitive( m_btn_local_allow_connections->get_active()); } void Gobby::PreferencesDialog::User::on_local_require_password_toggled() { m_grid_password->set_sensitive( m_btn_local_require_password->get_active()); } void Gobby::PreferencesDialog::User::on_local_keep_documents_toggled() { m_grid_local_documents_directory->set_sensitive( m_btn_local_keep_documents->get_active()); } Gobby::PreferencesDialog::Editor::Editor( const Glib::RefPtr& builder, Preferences& preferences) { builder->get_widget("tab-width", m_ent_tab_width); builder->get_widget("insert-spaces", m_btn_tab_spaces); builder->get_widget("automatic-indentation", m_btn_indentation_auto); builder->get_widget("smart-home-end", m_btn_homeend_smart); builder->get_widget("enable-autosave", m_btn_autosave_enabled); builder->get_widget("grid-autosave-interval", m_grid_autosave_interval); builder->get_widget("autosave-interval", m_ent_autosave_interval); const unsigned int tab_width = preferences.editor.tab_width; const bool tab_spaces = preferences.editor.tab_spaces; const bool indentation_auto = preferences.editor.indentation_auto; const bool homeend_smart = preferences.editor.homeend_smart; const unsigned int autosave_enabled = preferences.editor.autosave_enabled; const unsigned int autosave_interval = preferences.editor.autosave_interval; m_btn_autosave_enabled->signal_toggled().connect( sigc::mem_fun(*this, &Editor::on_autosave_enabled_toggled)); m_ent_tab_width->set_range(1, 8); m_ent_tab_width->set_value(tab_width); m_ent_tab_width->set_increments(1, 1); connect_option(*m_ent_tab_width, preferences.editor.tab_width); m_btn_tab_spaces->set_active(tab_spaces); connect_option(*m_btn_tab_spaces, preferences.editor.tab_spaces); m_btn_indentation_auto->set_active(indentation_auto); connect_option(*m_btn_indentation_auto, preferences.editor.indentation_auto); m_btn_homeend_smart->set_active(homeend_smart); connect_option(*m_btn_homeend_smart, preferences.editor.homeend_smart); m_btn_autosave_enabled->set_active(autosave_enabled); connect_option(*m_btn_autosave_enabled, preferences.editor.autosave_enabled); m_ent_autosave_interval->set_range(1, 60); m_ent_autosave_interval->set_value(autosave_interval); m_ent_autosave_interval->set_increments(1, 10); connect_option(*m_ent_autosave_interval, preferences.editor.autosave_interval); // Initial sensitivity on_autosave_enabled_toggled(); } void Gobby::PreferencesDialog::Editor::on_autosave_enabled_toggled() { m_grid_autosave_interval->set_sensitive( m_btn_autosave_enabled->get_active()); } Gobby::PreferencesDialog::View::View( const Glib::RefPtr& builder,Preferences& preferences) { builder->get_widget("enable-wrapping", m_btn_wrap_text); builder->get_widget("do-not-split-words", m_btn_wrap_words); builder->get_widget("display-line-numbers", m_btn_linenum_display); builder->get_widget("highlight-current-line", m_btn_curline_highlight); builder->get_widget("display-right-margin", m_btn_margin_display); builder->get_widget("grid-margin-position", m_grid_margin_pos); builder->get_widget("right-margin-column", m_ent_margin_pos); builder->get_widget("highlight-matching-brackets", m_btn_bracket_highlight); builder->get_widget_derived("display-whitespace", m_cmb_spaces_display); const Gtk::WrapMode mode = preferences.view.wrap_mode; const bool linenum_display = preferences.view.linenum_display; const bool curline_highlight = preferences.view.curline_highlight; const bool margin_display = preferences.view.margin_display; const unsigned int margin_pos = preferences.view.margin_pos; const bool bracket_highlight = preferences.view.bracket_highlight; m_btn_margin_display->signal_toggled().connect( sigc::mem_fun(*this, &View::on_margin_display_toggled)); m_btn_wrap_text->signal_toggled().connect( sigc::mem_fun(*this, &View::on_wrap_text_toggled)); m_ent_margin_pos->set_range(1, 1024); m_ent_margin_pos->set_value(margin_pos); m_ent_margin_pos->set_increments(1, 16); connect_option(*m_ent_margin_pos, preferences.view.margin_pos); m_btn_wrap_text->set_active(mode != Gtk::WRAP_NONE); m_btn_wrap_words->set_active(mode == Gtk::WRAP_WORD_CHAR); connect_wrap_option(*m_btn_wrap_text, *m_btn_wrap_words, preferences.view.wrap_mode); m_btn_linenum_display->set_active(linenum_display); connect_option(*m_btn_linenum_display, preferences.view.linenum_display); m_btn_curline_highlight->set_active(curline_highlight); connect_option(*m_btn_curline_highlight, preferences.view.curline_highlight); m_btn_margin_display->set_active(margin_display); connect_option(*m_btn_margin_display, preferences.view.margin_display); m_btn_bracket_highlight->set_active(bracket_highlight); connect_option(*m_btn_bracket_highlight, preferences.view.bracket_highlight); // TODO: When we require a higher version of GtkSourceView, then // we should add GTK_SOURCE_DRAW_SPACES_NBSP here. const int SOURCE_DRAW_SPACES = GTK_SOURCE_DRAW_SPACES_SPACE; m_cmb_spaces_display->set_option(preferences.view.whitespace_display); m_cmb_spaces_display->add( _("Display no whitespace"), static_cast(0)); m_cmb_spaces_display->add( _("Display spaces"), static_cast( SOURCE_DRAW_SPACES)); m_cmb_spaces_display->add( _("Display tabs"), static_cast( GTK_SOURCE_DRAW_SPACES_TAB)); m_cmb_spaces_display->add( _("Display tabs and spaces"), static_cast( SOURCE_DRAW_SPACES | GTK_SOURCE_DRAW_SPACES_TAB)); // Initial sensitivity on_wrap_text_toggled(); on_margin_display_toggled(); } void Gobby::PreferencesDialog::View::on_wrap_text_toggled() { m_btn_wrap_words->set_sensitive(m_btn_wrap_text->get_active()); } void Gobby::PreferencesDialog::View::on_margin_display_toggled() { m_grid_margin_pos->set_sensitive(m_btn_margin_display->get_active()); } Gobby::PreferencesDialog::Appearance::Appearance( const Glib::RefPtr& builder, Preferences& preferences) : m_scheme_list(Gtk::ListStore::create(m_scheme_columns)) { builder->get_widget_derived("toolbar-style", m_cmb_toolbar_style); builder->get_widget("font", m_btn_font); builder->get_widget("color-scheme-treeview", m_scheme_tree); const Pango::FontDescription& font = preferences.appearance.font; m_cmb_toolbar_style->set_option(preferences.appearance.toolbar_style); m_cmb_toolbar_style->add(_("Show text only"), Gtk::TOOLBAR_TEXT); m_cmb_toolbar_style->add(_("Show icons only"), Gtk::TOOLBAR_ICONS); m_cmb_toolbar_style->add(_("Show both icons and text"), Gtk::TOOLBAR_BOTH ); m_cmb_toolbar_style->add(_("Show text besides icons"), Gtk::TOOLBAR_BOTH_HORIZ ); m_btn_font->set_font_name(font.to_string()); m_conn_font.reset(new FontConnection( *m_btn_font, preferences.appearance.font)); Gtk::TreeViewColumn column_name; Gtk::CellRendererText renderer_name; column_name.pack_start(renderer_name, false); column_name.add_attribute(renderer_name.property_text(), m_scheme_columns.name); Pango::AttrList list; Pango::Attribute attr(Pango::Attribute::create_attr_weight( Pango::WEIGHT_BOLD)); list.insert(attr); renderer_name.property_attributes() = list; m_scheme_tree->append_column(column_name); m_scheme_tree->append_column(_("Scheme Description"), m_scheme_columns.description); m_scheme_tree->set_model(m_scheme_list); // Populate scheme list GtkSourceStyleSchemeManager* manager = gtk_source_style_scheme_manager_get_default(); const gchar* const* ids = gtk_source_style_scheme_manager_get_scheme_ids(manager); const Glib::ustring current_scheme = preferences.appearance.scheme_id; for (const gchar* const* id = ids; *id != NULL; ++id) { GtkSourceStyleScheme* scheme = gtk_source_style_scheme_manager_get_scheme( manager, *id); const gchar* name = gtk_source_style_scheme_get_name(scheme); const gchar* desc = gtk_source_style_scheme_get_description(scheme); Gtk::TreeIter iter = m_scheme_list->append(); (*iter)[m_scheme_columns.name] = name; (*iter)[m_scheme_columns.description] = desc; (*iter)[m_scheme_columns.scheme] = scheme; if (current_scheme == gtk_source_style_scheme_get_id(scheme)) m_scheme_tree->get_selection()->select(iter); } m_scheme_tree->get_selection()->signal_changed().connect( sigc::bind( sigc::mem_fun(*this, &Appearance::on_scheme_changed), sigc::ref(preferences))); m_scheme_list->set_sort_column(m_scheme_columns.name, Gtk::SORT_ASCENDING); } void Gobby::PreferencesDialog::Appearance::on_scheme_changed( Preferences& preferences) { Gtk::TreeIter iter = m_scheme_tree->get_selection()->get_selected(); GtkSourceStyleScheme* scheme = (*iter)[m_scheme_columns.scheme]; preferences.appearance.scheme_id = gtk_source_style_scheme_get_id(scheme); } Gobby::PreferencesDialog::Security::Security( const Glib::RefPtr& builder, FileChooser& file_chooser, Preferences& preferences, CertificateManager& cert_manager) : m_preferences(preferences), m_file_chooser(file_chooser), m_cert_manager(cert_manager) { builder->get_widget("trust-default-cas", m_btn_use_system_trust); builder->get_widget("additionally-trusted-cas", m_btn_path_trust_file); builder->get_widget("ca-error-message", m_error_trust_file); builder->get_widget_derived("secure-connection", m_cmb_connection_policy); builder->get_widget("authentication-none", m_btn_auth_none); builder->get_widget("authentication-certificate", m_btn_auth_cert); builder->get_widget("grid-auth-certificate", m_grid_auth_cert); builder->get_widget("private-key-file", m_btn_key_file); builder->get_widget("create-private-key", m_btn_key_file_create); builder->get_widget("key-error-message", m_error_key_file); builder->get_widget("certificate-file", m_btn_cert_file); builder->get_widget("create-certificate", m_btn_cert_file_create); builder->get_widget("cert-error-message", m_error_cert_file); m_cert_manager.signal_credentials_changed().connect( sigc::mem_fun(*this, &Security::on_credentials_changed)); m_btn_auth_cert->signal_toggled().connect( sigc::mem_fun(*this, &Security::on_auth_cert_toggled)); m_btn_use_system_trust->set_active( m_preferences.security.use_system_trust); m_btn_use_system_trust->signal_toggled().connect( sigc::mem_fun(*this, &Security::on_use_system_trust_toggled)); const std::string& trust_file = preferences.security.trusted_cas; if(!trust_file.empty()) m_btn_path_trust_file->set_filename(trust_file); m_conn_path_trust_file.reset(new PathConnection( *m_btn_path_trust_file, preferences.security.trusted_cas)); m_cmb_connection_policy->set_option(preferences.security.policy); /* m_cmb_connection_policy->add( _("Never use a secure connection"), INF_XMPP_CONNECTION_SECURITY_ONLY_UNSECURED);*/ m_cmb_connection_policy->add( _("Use TLS if possible"), INF_XMPP_CONNECTION_SECURITY_BOTH_PREFER_TLS); m_cmb_connection_policy->add( _("Always use TLS"), INF_XMPP_CONNECTION_SECURITY_ONLY_TLS); if(preferences.security.authentication_enabled) m_btn_auth_cert->set_active(true); else m_btn_auth_none->set_active(true); connect_option(*m_btn_auth_cert, preferences.security.authentication_enabled); const std::string& key_file = preferences.security.key_file; if(!key_file.empty()) m_btn_key_file->set_filename(key_file); m_conn_path_key_file.reset(new PathConnection( *m_btn_key_file, preferences.security.key_file)); m_btn_key_file_create->signal_clicked().connect( sigc::mem_fun(*this, &Security::on_create_key_clicked)); const std::string& cert_file = preferences.security.certificate_file; if(!cert_file.empty()) m_btn_cert_file->set_filename(cert_file); m_conn_path_cert_file.reset(new PathConnection( *m_btn_cert_file, preferences.security.certificate_file)); m_btn_cert_file_create->signal_clicked().connect( sigc::mem_fun(*this, &Security::on_create_cert_clicked)); // Initial sensitivity and content on_auth_cert_toggled(); on_credentials_changed(); } void Gobby::PreferencesDialog::Security::set_file_error(Gtk::Label& label, const GError* error) { if(error != NULL) { label.set_text(Glib::ustring::compose( _("Error reading file: %1"), error->message)); label.show(); } else { label.hide(); } } Gtk::Window& Gobby::PreferencesDialog::Security::get_toplevel(Gtk::Widget& widget) { Gtk::Window* parent = NULL; Gtk::Widget* toplevel_widget = widget.get_toplevel(); if(gtk_widget_is_toplevel(toplevel_widget->gobj())) parent = dynamic_cast(toplevel_widget); g_assert(parent != NULL); return *parent; } void Gobby::PreferencesDialog::Security::on_use_system_trust_toggled() { m_preferences.security.use_system_trust = m_btn_use_system_trust->get_active(); } void Gobby::PreferencesDialog::Security::on_credentials_changed() { set_file_error(*m_error_trust_file, m_cert_manager.get_trust_error()); if(m_key_generator_handle.get() == NULL) { set_file_error(*m_error_key_file, m_cert_manager.get_key_error()); } else { m_error_key_file->set_text( _("2048-bit RSA private key is being " "generated, please wait...")); m_error_key_file->show(); } if(m_cert_generator_handle.get() == NULL) set_file_error(*m_error_cert_file, m_cert_manager.get_certificate_error()); const bool operation_in_progress = m_key_generator_handle.get() != NULL || m_cert_generator_handle.get() != NULL; m_btn_key_file->set_sensitive(!operation_in_progress); m_btn_key_file_create->set_sensitive(!operation_in_progress); m_btn_cert_file->set_sensitive(!operation_in_progress); m_btn_cert_file_create->set_sensitive( !operation_in_progress && m_cert_manager.get_private_key() != NULL); } void Gobby::PreferencesDialog::Security::on_auth_cert_toggled() { m_grid_auth_cert->set_sensitive(m_btn_auth_cert->get_active()); } void Gobby::PreferencesDialog::Security::on_create_key_clicked() { m_file_dialog.reset(new FileChooser::Dialog( m_file_chooser, get_toplevel(*m_btn_key_file_create), _("Select a location for the generated key"), Gtk::FILE_CHOOSER_ACTION_SAVE)); const std::string& key_file = m_preferences.security.key_file; if(!key_file.empty()) m_file_dialog->set_filename(key_file); m_file_dialog->signal_response().connect( sigc::mem_fun( *this, &Security::on_file_dialog_response_key)); m_file_dialog->present(); } void Gobby::PreferencesDialog::Security::on_create_cert_clicked() { m_file_dialog.reset(new FileChooser::Dialog( m_file_chooser, get_toplevel(*m_btn_cert_file_create), _("Select a location for the generated certificate"), Gtk::FILE_CHOOSER_ACTION_SAVE)); const std::string& certificate_file = m_preferences.security.certificate_file; if(!certificate_file.empty()) m_file_dialog->set_filename(certificate_file); m_file_dialog->signal_response().connect( sigc::mem_fun( *this, &Security::on_file_dialog_response_certificate)); m_file_dialog->present(); } void Gobby::PreferencesDialog::Security::on_file_dialog_response_key( int response_id) { const std::string filename = m_file_dialog->get_filename(); m_file_dialog.reset(NULL); if(response_id == Gtk::RESPONSE_ACCEPT) { m_key_generator_handle = create_key( GNUTLS_PK_RSA, 2048, sigc::bind( sigc::mem_fun( *this, &Security::on_key_generated), filename)); on_credentials_changed(); } } void Gobby::PreferencesDialog::Security::on_file_dialog_response_certificate( int response_id) { const std::string filename = m_file_dialog->get_filename(); m_file_dialog.reset(NULL); if(response_id == Gtk::RESPONSE_ACCEPT && m_cert_manager.get_private_key() != NULL) { m_cert_generator_handle = create_self_signed_certificate( m_cert_manager.get_private_key(), sigc::bind( sigc::mem_fun( *this, &Security::on_cert_generated), filename)); on_credentials_changed(); } } void Gobby::PreferencesDialog::Security::on_key_generated( const KeyGeneratorHandle* handle, gnutls_x509_privkey_t key, const GError* error, const std::string& filename) { m_key_generator_handle.reset(NULL); m_cert_manager.set_private_key(key, filename.c_str(), error); } void Gobby::PreferencesDialog::Security::on_cert_generated( const CertificateGeneratorHandle* hndl, gnutls_x509_crt_t cert, const GError* error, const std::string& filename) { m_cert_generator_handle.reset(NULL); if(cert != NULL) { // Note this needs to be allocated with g_malloc, since it // is directly passed and freed by libinfinity gnutls_x509_crt_t* crt_array = static_cast( g_malloc(sizeof(gnutls_x509_crt_t))); crt_array[0] = cert; m_cert_manager.set_certificates( crt_array, 1, filename.c_str(), error); } else { m_cert_manager.set_certificates( NULL, 0, filename.c_str(), error); } } Gobby::PreferencesDialog::PreferencesDialog( const Glib::RefPtr& builder, FileChooser& file_chooser, Preferences& preferences, CertificateManager& cert_manager ): Gtk::Dialog(GTK_DIALOG(gtk_builder_get_object(builder->gobj(), "PreferencesDialog"))), m_page_user(builder, preferences), m_page_editor(builder, preferences), m_page_view(builder, preferences), m_page_appearance(builder, preferences), m_page_security(builder, file_chooser, preferences, cert_manager) { add_button(_("_Close"), Gtk::RESPONSE_CLOSE); } std::unique_ptr Gobby::PreferencesDialog::create(Gtk::Window& parent, FileChooser& file_chooser, Preferences& preferences, CertificateManager& cert_manager) { Glib::RefPtr builder = Gtk::Builder::create_from_resource( "/de/0x539/gobby/ui/preferences-dialog.ui"); std::unique_ptr dialog( new PreferencesDialog(builder, file_chooser, preferences, cert_manager)); dialog->set_transient_for(parent); return dialog; } void Gobby::PreferencesDialog::on_response(int id) { hide(); } code/dialogs/preferences-dialog.hpp000066400000000000000000000301211304552132100176520ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_PREFERENCESDIALOG_HPP_ #define _GOBBY_PREFERENCESDIALOG_HPP_ #include "core/preferences.hpp" #include "core/filechooser.hpp" #include "core/certificatemanager.hpp" #include "core/credentialsgenerator.hpp" #include "core/huebutton.hpp" #include #include #include #include #include #include #include #include #include #include #include namespace Gobby { template class PreferencesComboBox: public Gtk::ComboBox { public: PreferencesComboBox(GtkComboBox* box, const Glib::RefPtr& builder): Gtk::ComboBox(box), m_option(NULL), m_store(Gtk::ListStore::create(m_columns)) { set_model(m_store); Gtk::CellRendererText* renderer = Gtk::manage(new Gtk::CellRendererText); pack_start(*renderer, true); add_attribute(renderer->property_text(), m_columns.text); } void set_option(Preferences::Option& option) { m_option = &option; } void add(const Glib::ustring& text, const OptionType& value) { Gtk::TreeIter iter = m_store->append(); (*iter)[m_columns.text] = text; (*iter)[m_columns.value] = value; if(*m_option == value) set_active(iter); } private: class Columns: public Gtk::TreeModelColumnRecord { public: Gtk::TreeModelColumn text; Gtk::TreeModelColumn value; Columns() { add(text); add(value); } }; virtual void on_changed() { Gtk::ComboBox::on_changed(); OptionType value = (*get_active())[m_columns.value]; if(*m_option != value) *m_option = value; } Preferences::Option* m_option; Columns m_columns; Glib::RefPtr m_store; }; class PreferencesDialog : public Gtk::Dialog { private: PreferencesDialog(const Glib::RefPtr& builder, FileChooser& file_chooser, Preferences& preferences, CertificateManager& cert_manager); public: static std::unique_ptr create( Gtk::Window& parent, FileChooser& file_chooser, Preferences& preferences, CertificateManager& cert_manager); // An object which keeps two values in sync with each other, using // notification signals of both objects. template class DuplexConnection { public: template DuplexConnection(Sig1 sig1, Sig2 sig2) { m_conn1 = sig1.connect(sigc::mem_fun(*this, &DuplexConnection::changed1)); m_conn2 = sig2.connect(sigc::mem_fun(*this, &DuplexConnection::changed2)); } virtual ~DuplexConnection() { m_conn1.disconnect(); m_conn2.disconnect(); } void block() { m_conn1.block(); m_conn2.block(); } void unblock() { m_conn1.unblock(); m_conn2.unblock(); } private: void changed1() { m_conn2.block(); if(get2() != get1()) set2(get1()); m_conn2.unblock(); } void changed2() { m_conn1.block(); if(get1() != get2()) set1(get2()); m_conn1.unblock(); } protected: virtual Type get1() const = 0; virtual Type get2() const = 0; virtual void set1(const Type& t) = 0; virtual void set2(const Type& t) = 0; private: sigc::connection m_conn1; sigc::connection m_conn2; }; class FontConnection: public DuplexConnection { public: FontConnection( Gtk::FontButton& font_button, Preferences::Option& option) : DuplexConnection( font_button.signal_font_set(), option.signal_changed()), m_font_button(font_button), m_option(option) { } protected: virtual Pango::FontDescription get1() const { return Pango::FontDescription( m_font_button.get_font_name()); } virtual Pango::FontDescription get2() const { return m_option.get(); } virtual void set1(const Pango::FontDescription& val) { m_font_button.set_font_name(val.to_string()); } virtual void set2(const Pango::FontDescription& val) { m_option.set(val); } private: Gtk::FontButton& m_font_button; Preferences::Option& m_option; }; // Note that old GTK+ versions do not emit the file-set signal // of Gtk::FileChooserButton when a new entry is chosen from // the combo box, but only from the dialog. The bug was fixed // only in GTK+ 3.10.0. // // Therefore, we are using the selection-changed signal // instead. However, when the button is constructed, the // signal is emitted "too often" since it is first emitted // with the home folder of the current user, and then again // with what was specified when set_filename() is called // to set the initial name. In the case of the local documents // directory, this causes a significant overhead and running // sessions being closed once the preferences dialog is opened. // // To work around this, we ignore all signal emissions until // the first signal emission with the initially set value occurs. // Once we depend on GTK+ 3.10 or greater, the workaround // can be removed and we can switch to signal_file_set(). class PathConnection: public DuplexConnection { public: PathConnection(Gtk::FileChooser& file_chooser, Preferences::Option& option): DuplexConnection( file_chooser.signal_selection_changed(), option.signal_changed()), m_file_chooser(file_chooser), m_option(option), m_initial_value(option), m_seen_initial_value(file_chooser.get_filename() == m_initial_value) { } protected: virtual std::string get1() const { return m_file_chooser.get_filename(); } virtual std::string get2() const { if(m_seen_initial_value) return m_option.get(); else return ""; } virtual void set1(const std::string& val) { m_file_chooser.set_filename(val); } virtual void set2(const std::string& val) { if(!m_seen_initial_value) { if(val == m_initial_value) m_seen_initial_value = true; } else { m_option.set(val); } } private: Gtk::FileChooser& m_file_chooser; Preferences::Option& m_option; const std::string m_initial_value; bool m_seen_initial_value; }; class User { public: User(const Glib::RefPtr& builder, Preferences& preferences); protected: void on_local_allow_connections_toggled(); void on_local_require_password_toggled(); void on_local_keep_documents_toggled(); Gtk::Entry* m_ent_user_name; HueButton* m_btn_user_color; Gtk::Scale* m_scl_user_alpha; Gtk::CheckButton* m_btn_remote_show_cursors; Gtk::CheckButton* m_btn_remote_show_selections; Gtk::CheckButton* m_btn_remote_show_current_lines; Gtk::CheckButton* m_btn_remote_show_cursor_positions; Gtk::Grid* m_grid_local_connections; Gtk::CheckButton* m_btn_local_allow_connections; Gtk::Grid* m_grid_password; Gtk::CheckButton* m_btn_local_require_password; Gtk::Entry* m_ent_local_password; Gtk::SpinButton* m_ent_local_port; Gtk::CheckButton* m_btn_local_keep_documents; Gtk::Grid* m_grid_local_documents_directory; Gtk::FileChooserButton* m_btn_local_documents_directory; std::unique_ptr m_conn_local_documents_directory; }; class Editor { public: Editor(const Glib::RefPtr& builder, Preferences& preferences); protected: void on_autosave_enabled_toggled(); Gtk::SpinButton* m_ent_tab_width; Gtk::CheckButton* m_btn_tab_spaces; Gtk::CheckButton* m_btn_indentation_auto; Gtk::CheckButton* m_btn_homeend_smart; Gtk::CheckButton* m_btn_autosave_enabled; Gtk::Grid* m_grid_autosave_interval; Gtk::SpinButton* m_ent_autosave_interval; }; class View { public: View(const Glib::RefPtr& builder, Preferences& preferences); protected: void on_wrap_text_toggled(); void on_margin_display_toggled(); Gtk::CheckButton* m_btn_wrap_text; Gtk::CheckButton* m_btn_wrap_words; Gtk::CheckButton* m_btn_linenum_display; Gtk::CheckButton* m_btn_curline_highlight; Gtk::CheckButton* m_btn_margin_display; Gtk::Grid* m_grid_margin_pos; Gtk::SpinButton* m_ent_margin_pos; Gtk::CheckButton* m_btn_bracket_highlight; PreferencesComboBox* m_cmb_spaces_display; }; class Appearance { public: Appearance(const Glib::RefPtr& builder, Preferences& preferences); protected: class SchemeColumns: public Gtk::TreeModelColumnRecord { public: Gtk::TreeModelColumn scheme; Gtk::TreeModelColumn name; Gtk::TreeModelColumn description; SchemeColumns() { add(scheme); add(name); add(description); } }; void on_pref_font_changed(); void on_scheme_changed(Preferences& preferences); PreferencesComboBox* m_cmb_toolbar_style; Gtk::FontButton* m_btn_font; std::unique_ptr m_conn_font; SchemeColumns m_scheme_columns; Glib::RefPtr m_scheme_list; Gtk::TreeView* m_scheme_tree; }; class Security { public: Security(const Glib::RefPtr& builder, FileChooser& file_chooser, Preferences& preferences, CertificateManager& cert_manager); protected: void set_file_error(Gtk::Label& label, const GError* error); Gtk::Window& get_toplevel(Gtk::Widget& widget); void on_use_system_trust_toggled(); void on_credentials_changed(); void on_auth_cert_toggled(); void on_create_key_clicked(); void on_create_cert_clicked(); void on_file_dialog_response_key(int response_id); void on_file_dialog_response_certificate(int response_id); void on_key_generated(const KeyGeneratorHandle* handle, gnutls_x509_privkey_t key, const GError* error, const std::string& filename); void on_cert_generated(const CertificateGeneratorHandle* hndl, gnutls_x509_crt_t cert, const GError* error, const std::string& filename); Preferences& m_preferences; FileChooser& m_file_chooser; CertificateManager& m_cert_manager; Gtk::CheckButton* m_btn_use_system_trust; Gtk::FileChooserButton* m_btn_path_trust_file; std::unique_ptr m_conn_path_trust_file; Gtk::Label* m_error_trust_file; PreferencesComboBox* m_cmb_connection_policy; Gtk::RadioButton* m_btn_auth_none; Gtk::RadioButton* m_btn_auth_cert; Gtk::Grid* m_grid_auth_cert; Gtk::FileChooserButton* m_btn_key_file; std::unique_ptr m_conn_path_key_file; Gtk::Button* m_btn_key_file_create; Gtk::Label* m_error_key_file; Gtk::FileChooserButton* m_btn_cert_file; std::unique_ptr m_conn_path_cert_file; Gtk::Button* m_btn_cert_file_create; Gtk::Label* m_error_cert_file; std::unique_ptr m_key_generator_handle; std::unique_ptr m_cert_generator_handle; std::unique_ptr m_file_dialog; }; protected: virtual void on_response(int id); User m_page_user; Editor m_page_editor; View m_page_view; Appearance m_page_appearance; Security m_page_security; }; } #endif // _GOBBY_PREFERENCESDIALOG_HPP_ code/main.cpp000066400000000000000000000031651304552132100134210ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "features.hpp" #include "application.hpp" int main(int argc, char* argv[]) { Glib::RefPtr application = Gobby::Application::create(); const int result = application->run(argc, argv); // In GTK+ 3.12, there is a reference leak present which makes us // end up with one too much reference on the application object. // This bug was fixed for GTK+ 3.14. // https://bugzilla.gnome.org/show_bug.cgi?id=730383 // // We need the application object to be finalized correctly, so that // its destructor runs and the preferences are serialized to disk. // Therefore, this hack makes sure that the application object ends up // with the expected reference count at this point. if(G_OBJECT(application->gobj())->ref_count > 1) application->unreference(); return result; } code/operations/000077500000000000000000000000001304552132100141475ustar00rootroot00000000000000code/operations/Makefile.am000066400000000000000000000012661304552132100162100ustar00rootroot00000000000000gobby_0_5_SOURCES += \ code/operations/operations.cpp \ code/operations/operation-delete.cpp \ code/operations/operation-export-html.cpp \ code/operations/operation-new.cpp \ code/operations/operation-open.cpp \ code/operations/operation-open-multiple.cpp \ code/operations/operation-save.cpp \ code/operations/operation-subscribe-path.cpp noinst_HEADERS += \ code/operations/operations.hpp \ code/operations/operation-delete.hpp \ code/operations/operation-export-html.hpp \ code/operations/operation-new.hpp \ code/operations/operation-open.hpp \ code/operations/operation-open-multiple.hpp \ code/operations/operation-save.hpp \ code/operations/operation-subscribe-path.hpp code/operations/operation-delete.cpp000066400000000000000000000044451304552132100201220ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "operations/operation-delete.hpp" #include "util/i18n.hpp" Gobby::OperationDelete::OperationDelete(Operations& operations, InfBrowser* browser, const InfBrowserIter* iter): Operation(operations), m_browser(browser), m_iter(*iter), m_name(inf_browser_get_node_name(browser, iter)), m_request(NULL) { g_object_ref(browser); } Gobby::OperationDelete::~OperationDelete() { if(m_request != NULL) { g_signal_handlers_disconnect_by_func( G_OBJECT(m_request), (gpointer)G_CALLBACK(on_request_finished_static), this); g_object_unref(m_request); get_status_bar().remove_message(m_message_handle); } g_object_unref(m_browser); } void Gobby::OperationDelete::start() { InfRequest* request = inf_browser_remove_node( m_browser, &m_iter, on_request_finished_static, this ); // Object might be gone at this point, so check stack variable if(request != NULL) { m_request = request; // Note infc_browser_remove_node does not return a // new reference. g_object_ref(m_request); m_message_handle = get_status_bar().add_info_message( Glib::ustring::compose( _("Removing node \"%1\"..."), m_name)); } } void Gobby::OperationDelete::on_request_finished(const GError* error) { if(error) { get_status_bar().add_error_message( Glib::ustring::compose( _("Failed to delete node \"%1\""), m_name), error->message); fail(); } else { finish(); } } code/operations/operation-delete.hpp000066400000000000000000000034131304552132100201210ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_OPERATIONS_OPERATIONDELETE_HPP_ #define _GOBBY_OPERATIONS_OPERATIONDELETE_HPP_ #include "operations/operations.hpp" namespace Gobby { class OperationDelete: public Operations::Operation { public: OperationDelete(Operations& operations, InfBrowser* browser, const InfBrowserIter* iter); virtual ~OperationDelete(); virtual void start(); protected: static void on_request_finished_static(InfRequest* request, const InfRequestResult* result, const GError* error, gpointer user_data) { static_cast(user_data)-> on_request_finished(error); } void on_request_finished(const GError* error); protected: InfBrowser* m_browser; const InfBrowserIter m_iter; const Glib::ustring m_name; InfRequest* m_request; StatusBar::MessageHandle m_message_handle; }; } #endif // _GOBBY_OPERATIONS_OPERATIONDELETE_HPP_ code/operations/operation-export-html.cpp000066400000000000000000000446071304552132100211470ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ // Include this first because some of the other headers include X11.h which, // among others, #defines "None". #include #include "operations/operation-export-html.hpp" #include "util/i18n.hpp" #include #include #include #include #include #include #include #include namespace { char const gobby_icon[] = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAAN1wAADdcBQiibeAAAAAd0SU1FB9gMEQwLEOi12dIAAAvuSURBVGje7Zl/cFTXdcc/b997u/t2V9Ki30hCEBAGgw3IEGPiH+MQGzshdpO4hcSJcezG+eEJdiF4HNdpaNJmOm7tIW7dpiWexh2Pm4KTVrELFv4RG8e4EyaG8MMFYSQLIfQDSfv713vv3nf7xy5CgMBIns44M5yZN3vvvrv7zvecc7/nnvPgklySS/IHLdrFLJozZ862WbNmhfP5fEBKqSulFIBSStm27Yxda5qmCAQCDiiKqyCbzVqe540+y/M8T9O0stqaWr19R/uisb9fDKYGV+gw1Az9z4O8kG7GxQBobW2d+vTTT7cK4QJQURFFKTV6lcCUxkXFT4/Hn+/7/T62b9tOMBB8pu2Ftq+eelZlyP/mvAUfaw0GzMTRroGeT55M3/W6bXecTzffxbqqUChg2/ZZyjI6h1NAOGs8/lxKyYaHNlBXV7tm9erVPwBYBNGli65sPDmY9EL1U+oarpj+8TkLpr362ZqKRz40gFAohGVZZ3x3JgjGAXHK+mfOi2EkUUqx6clNmqEb31+zZs2DU0P+9UsWzJvWWl9r7vvN/6Z9SnlXLprZ1DKzfu1iqJh0CJXiGM+T+P1+crkcsVjsvJ4YL2T8fj9VVZWj923bwfO8Iogfb+LuNXdvarls9nErYFKWixvXRwNlb+/tzMS7BrR8QVSaun4rUm6ZDAB/PB4P9vT00NjYMK71zw2TC1sfFMpTDJ8cRnoCx3b54oqbNXf3G82J7i6UUhi6xtLqcGTEkfRkcyPKJ4+Nt50vxEJlwH/5NO06TSPg8/nw+XzMnXs5L7W3U1tbi5SS/v7+D7T+2fO6ulpeefkVuru7kZ6H49ic2P4if3LdEt5/42Vcx0UqRUJ4OJ7HO73xX76Uc/54IiHkB+Kahs9TSkOB9CQg2bd/P42NjSSTSQKBwOSsr+CWW29BKUU2m2Okv4/X9/+OdF8P0nVRgKsUtlQci+eOpXLOAxOl0acAXUNDoc65qZRi1apVvPjiCzQ0NNDV1cXAwMAYRU9b+7TSiunTZ1BXV3sW/Xr8atMTLGmqo3/3LjzAQ5GTCqUkQ9nC9l3QNxEAUeA+AE+p88ZXe3s7x4/30tTUSGNjI+Xl5WdY+OywOTWOxWKjRgiFQgjXRc8kEX4XJ5tBKRCewvYUnSO5w5m8+/BEE9mnAeZPDyGkIpUVOELheQpdA7+ukbI9MgWPLVu2sH79egqFPENDJ8coejYrje+Z+vo63tq6hZaqChLvH8YrriArFa6QIpG3/30XpCcK4JMAf/2VaXz8YyFiCZvB4QL5rIOlQ01Ep3PI5Y5/6aOtrY0VK1ZcgE7PVdzvN5k9u2V0feeuN1naVE33yHDR+krhKsXR4cxBuyD+5oMocjwAMwAWNgYRtiCsK5ordETAoHPIZeObFstuXsVzzzTyzt4D/OL5/+C+r3+TYNC6KBAAQgg0TaPrwH6mhvzEOo9wKpdnhSJdELm44zz5BojJAAgBaEIghUQ6EukIfrUvR+0t63hq/fXU1ZSDUnz+thsYOJniz7+7jm8/8BC6bpxH8bFMVbzX3NzMqz/dTGtVlGOH96AAobyi9Ucy+35dkM9cTIIdD0APcK2wXTzXQ7qSPccKTP/cI6z89A2ELKt4AtEUhubSVF/OM5v/joe//xTfuv/+szbwmSxkmv5RFkonEvgLGdInBlBecW1GegymC6mUEI9e7AlhvLPQd6KWL+vmBcIWKE/xXGc9n7rxGkJWCLQg+EqXFgCfju04zG2pwTBMLCuIZQUJBq3SZ4BAIEAwGCQQ8I+G0Us/3cysmijJ3mMl6yuEp+hK5A/sLIjXLxbAeB7oD5i+ff1x5xNHT7p847lBnv3XtVRWVXOoo5NoRTmapoOmoaTAFQ7JZJoVN9/Atu3bWLBg4Rl0ejqcVIl56qmvr2f40EHqgh7SKSaujJQcz3nqPdt+bCIFzXgAIrG8vnfjf8euWb044ps31c/Sq1t5/j+3UzjwE37fVcD2Qhg+HVNmqQnkeWV/mi07XmNkeIjp05vHVfzUZ1VVFb97eQfN5RaJ9w4Ws66ncKVi2CqX78uhY5MGoOv6581AaPOia28N+zTb9+qRtxjJSmqqKnn33cOsX1rJbUtObWyJsMOIgp+hmEM6nSGZjJFOp8ehz6LymqZRVVXF7ue3sKjCoC+bLjKP9IgFowRa5jkc6WRSAAJW2WPR6tpvfuEb3ysvn1KL69pkD9WR73gWTfeRy9lIV+AJD0/I0UtJj5AJjuPgOC6pVPq8zKOU4n927iSqQ/L9TpQqnnny0qP5xhsZTqbURGtiAyAYijzaOPPy+//ovkciuk9HCBelFPUtS/jhj+bjieI5VjoST3qj7FQE4/HekCQQDFJbW0Nz87QL0ufWf/sZ88oCDHaMoICc8IiHq/nq+u+wZ+NGJgNgVtAKP/SZu9ZFlJS4QhQf5kn60i6RCgkaRCIW0hF4UhUVd4uekMKjMxFA13XePdRBX1/fuAc5pRTCdRFDA+RFCoXCUYq0hPmfuY2AZaEmbH/wVVbXfHvx8s+Vg8J1HYTrIFwb4Tro/jAjGZvuY73Mn3cZ299JFr3glpKc67GvJ0/M9giHQuzZcwDQME0T0zQwDAPDMEtzkwM72mmJhkkP9KEU5KQiU9nADV/68qinJuwBTdNXN82ap7nO6YIdpVDKwy7k2P3mS4hkF48+vJYV//AkVcEsCxpMpOvR0Vfgr3Y7LLx1Mf39A2ScJMeP97Bs2TI0TTuncxE79C4NWhalFLZSJDwf1999L4ZpTsr6AD4pZblhBnAdG9exEa6N69ok40PsenEzn12znG2//g3hcIiFdyzn74+Uc+ezce76eZy/2KdRf89CWiLT+NFj/0h0VZQHvvcgO3bsQNd1IpHI6OXXfSR7O0n19qBQ5KVCNMzk6pUrS7WxNzkPoKEJxy4Wl6pUP3mSd157jns23kllYyX3/O2fcucPHqHluhYic5eTKWRI2SkyToZUIcUL7k6CC4MEQ0HMr5hsfPwvqampYenSpZimCUA6MUTDnGb2vr2XyyJ+hvDzhYe+C0A8nhhT3U3QA4mRodDJE11FD7g2wrHpPbKbK2+aA1NgMD1IQiRovr6ZpEgSy8VIFBKkCilShRRpO014Rhg9pBdziaUTujnET372z3R1dY2GZXKoj/lXzaVp2eW8VZDIqXXMuuqqYgUVrZh0a9G46aabtu597RfLM7lceW3DDBWtbfJnkp3anFU30JfqQ3oSV7rY0qbgFsi7eXJujpyToyAK4/6p1WzRsbODZDKJ67oYhkH6ZB/xdI6MBl9+eC1X337vaB0dDofG6XJcJICVK1f+2cqVK5eOjIxcc+TIkXmxWKxpyE4vHBEjPi/tjQJwpIMtbGxhn1fxsSL9EsdxyOfzlJWV8fZbb+CzU9yxdiNTZ10xppunSCbThMOhyQFYt25dP9AGtD3++OPVpmlee/CfDv68P9VveaoIQHgC4YmJxablI5FI4DgOSin0yhl8cc29hCJl57QaLSuApmmT3MRjZMOGDcNPPPHEnvRIWhvuHSZQM7mNJbKC5PEkhmHi8xVP7Pd86wE6OjpIpVKYph9d92FZFpZlEY1GPwQLnSWVlZVDt6+4/Ze/3fbbTwymBquNKYYebApaRr2h6ZaOHtTRreKGlXmJLEi8goeX9GBQIYYl1RVVfP1L91FZWVnqpyp8Ph+zZ8/m6NGjJBKJYiLL5XBdB9eVBAIBstmsBlgf+v3A1q1b9b6+vhal1JUnTpxY1N3d/WAwEoyksilSmRSZTAZN0wiHwkTCEcrDZUytbWDG9Bk0NTURDAYIhcJUVFRgmsY5jd9CIc/w8DDxeAIpBUIIbNumvX2H29bW9jXgMJAC4sAIF6iNLyrwZs6c+V5ra+sUv9/vMwxDMwxDA6UphXJdVwkhlJRSua6rHMf2zmylM24P9dQax3F8tl3QfD5N7+09EejuPvY1oPvs3jKQKIFJTxhASeqB8P/j2yIdqAaqgNiFtthY72h8NCUMTC2BMc7jxgygfVQBjI2QypL3K0pzCRRKII9/1AGc8f4QqAOCJRDJDwi1S3JJLskfgvwfcPxaSBSG+m4AAAAASUVORK5CYII="; // Sort tags so that CSS declaration order corresponds to priority struct TagComparator { bool operator()(GtkTextTag* first, GtkTextTag* second) const { return gtk_text_tag_get_priority(first) < gtk_text_tag_get_priority(second); } }; typedef std::set priority_tag_set; // We don't use Glib::ustring::compose for now because // it's formatting support does not compile properly under // Windows. See https://bugzilla.gnome.org/show_bug.cgi?id=599340 Glib::ustring uprintf(gchar const* fmt, ...) { va_list args; va_start(args, fmt); gchar* str = g_strdup_vprintf(fmt, args); va_end(args); Glib::ustring result; try { result = str; } catch (...) { g_free(str); throw; } g_free(str); return result; } unsigned int rgba_to_rgb24(const GdkRGBA* rgba) { const guint8 red = static_cast(rgba->red * 255.0 + 0.5); const guint8 green = static_cast(rgba->green * 255.0 + 0.5); const guint8 blue = static_cast(rgba->blue * 255.0 + 0.5); return (red << 16) | (green << 8) | blue; } Glib::ustring get_current_tags(priority_tag_set& tags, GtkTextIter* iter) { GSList* current_tags = gtk_text_iter_get_tags(iter); // make sure to free current_tags in an exception-safe manner: Glib::SListHandle > handle( current_tags, Glib::OWNERSHIP_SHALLOW); Glib::ustring classes; for(GSList* tag = current_tags; tag != 0; tag = tag->next) { if(!classes.empty()) classes += ' '; classes += uprintf( "tag_%p", static_cast(tag->data)); tags.insert(GTK_TEXT_TAG(tag->data)); } return classes; } // write the Gtk::TextBuffer from document into content, inserting // s for line breaks and authorship of chunks of text, also // save all users and tags encountered and the total number of // lines dumped void dump_buffer(Gobby::TextSessionView& view, xmlpp::Element* content, std::set& users, priority_tag_set& tags, unsigned int& line_counter) { using namespace Gobby; users.clear(); tags.clear(); line_counter = 1; xmlpp::Element* last_node = content; xmlpp::Element* line_no = last_node->add_child("span"); line_no->set_attribute("class", "line_no"); line_no->set_attribute("id", "line_1"); GtkTextBuffer* buffer = GTK_TEXT_BUFFER( view.get_text_buffer()); InfTextGtkBuffer* inf_buffer = INF_TEXT_GTK_BUFFER( inf_session_get_buffer( INF_SESSION(view.get_session()))); GtkTextIter begin; gtk_text_buffer_get_start_iter(buffer, &begin); { GtkTextIter end; gtk_text_buffer_get_end_iter(buffer, &end); gtk_source_buffer_ensure_highlight( GTK_SOURCE_BUFFER(buffer), &begin, &end); } // iterate through chunks of text during which the currently // set tags do not change, write each as a while(!gtk_text_iter_is_end(&begin)) { // add current tags as classes for CSS formatting // (both for author of text and syntax highlighting) Glib::ustring classes = get_current_tags(tags, &begin); if(!classes.empty()) { last_node = last_node->add_child("span"); last_node->set_attribute("class", classes); // add mouseover "written by" popup // this only needs to happen when there are tags, // because the presence of an author implies a tag InfTextUser* user = inf_text_gtk_buffer_get_author( inf_buffer, &begin); if(user) { char const* user_name = inf_user_get_name( INF_USER(user)); last_node->set_attribute( "title", uprintf(_("written by: %s"), user_name)); users.insert(user); } } GtkTextIter next = begin; gtk_text_iter_forward_to_tag_toggle(&next, 0); // split text by newlines so we can // insert line number elements gchar* text = gtk_text_iter_get_text(&begin, &next); try { gchar const* last_pos = text; for(gchar const* i = last_pos; *i; ++i) { if(*i != '\n') continue; ++line_counter; gchar const* next_pos = i; ++next_pos; last_node->add_child_text( Glib::ustring(last_pos, next_pos)); last_pos = next_pos; line_no = last_node->add_child("span"); line_no->set_attribute("class", "line_no"); line_no->set_attribute( "id", uprintf("line_%d", line_counter)); } last_node->add_child_text( Glib::ustring(last_pos)); } catch(...) { g_free(text); throw; } g_free(text); // if we do not have any tags, we did not add classes // and consequently did not go into a new span if(!classes.empty()) last_node = last_node->get_parent(); begin = next; } } // some random interesting information/advertisement to be put at // the end of the html output void dump_info(xmlpp::Element* node, Gobby::TextSessionView& view) { using namespace Gobby; // put current time char const* time_str; int const n = 128; char buf[n]; { std::time_t now; std::time(&now); // TODO: localtime is not threadsafe if(std::strftime(buf, n, "%c", localtime(&now))) time_str = buf; else time_str = _(""); } char const* hostname = view.get_hostname().c_str(); char const* path = view.get_path().c_str(); char const* translated = // %1$s is session name/hostname // %2$s is path within the session // %3$s is current date as formatted by %c, // %4$s is a link to the gobby site, it must be present because // we need to handle that manually to insert a hyperlink // instead of just printf'ing it. _("Document generated from %1$s:%2$s at %3$s by %4$s"); char const* p = std::strstr(translated, "%4$s"); g_assert(p); node->add_child_text( uprintf(Glib::ustring(translated, p).c_str(), hostname, path, time_str)); xmlpp::Element* link = node->add_child("a"); link->set_attribute("href", "http://gobby.github.io/"); link->add_child_text(PACKAGE_STRING); if(*p != '\0') node->add_child_text( uprintf(p+4 , hostname, path, time_str)); } // list each author before the actual text void dump_user_list(xmlpp::Element* list, const std::set& users) { for(std::set::const_iterator i = users.begin(); i != users.end(); ++i) { // TODO: Get the S and V from the InfTextGtkBuffer // setting? double hue = inf_text_user_get_hue(*i); double sat = 0.35; double val = 1.0; double r, g, b; gtk_hsv_to_rgb(hue, sat, val, &r, &g, &b); Gdk::RGBA rgba; rgba.set_rgba(r, g, b, 1.0); const char* name = inf_user_get_name(INF_USER(*i)); const unsigned int rgb = rgba_to_rgb24(rgba.gobj()); xmlpp::Element* item = list->add_child("li"); item->add_child_text(name); item->set_attribute( "style", uprintf("background-color: #%06x;", rgb)); } } void dump_tags_style(xmlpp::Element* css, const priority_tag_set& tags) { for(priority_tag_set::const_iterator i = tags.begin(); i != tags.end(); ++i) { GdkRGBA* fg, * bg; gint weight; gboolean underline; PangoStyle style; gboolean fg_set, bg_set, weight_set, underline_set, style_set; g_object_get(G_OBJECT(*i), "background-rgba", &bg, "foreground-rgba", &fg, "weight", &weight, "underline", &underline, "style", &style, "background-set", &bg_set, "foreground-set", &fg_set, "weight-set", &weight_set, "underline-set", &underline_set, "style-set", &style_set, NULL); unsigned int bg_rgb, fg_rgb; if(fg_set) fg_rgb = rgba_to_rgb24(fg); if(bg_set) bg_rgb = rgba_to_rgb24(bg); gdk_rgba_free(fg); gdk_rgba_free(bg); css->add_child_text( uprintf(".tag_%p {\n", static_cast(*i))); if(fg_set) css->add_child_text(uprintf( " color: #%06x;", fg_rgb)); if(bg_set) css->add_child_text(uprintf( " background-color: #%06x;", bg_rgb)); if(weight_set) css->add_child_text(uprintf( " font-weight: %d;", weight)); if(underline_set) css->add_child_text(uprintf( " text-decoration: %s;", underline ? "underline" : "none")); if(style_set) css->add_child_text(uprintf( " font-style: %s;", (style == PANGO_STYLE_ITALIC) ? "italic" : "none")); css->add_child_text("}\n"); } } // generate xhtml representation of the document and write it to the // specified location in the filesystem std::string export_html(Gobby::TextSessionView& view) { using namespace Gobby; xmlpp::Document output; output.set_internal_subset("html", "-//W3C//DTD XHTML 1.1//EN", "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"); xmlpp::Element * root = output.create_root_node( "html", "http://www.w3.org/1999/xhtml"), * head = root->add_child("head"), * body = root->add_child("body"), * title = head->add_child("title"), * style = head->add_child("style"), * h1 = body->add_child("h1"), * h2 = body->add_child("h2"), * user_list = body->add_child("ul"), * content = body->add_child("pre"), * info = body->add_child("p"), * icon = h1->add_child("img"); icon->set_attribute("src", gobby_icon); icon->set_attribute("width", "48"); icon->set_attribute("height", "48"); icon->set_attribute("alt", "a gobby document:"); icon->set_attribute("class", "icon"); const Glib::ustring& document_name = view.get_title(); title->add_child_text(document_name + " - infinote document"); h1->add_child_text(document_name); content->set_attribute("class", "document"); std::set users; priority_tag_set tags; unsigned int line_counter; dump_buffer(view, content, users, tags, line_counter); h2->add_child_text(_("Participants")); info->set_attribute("class", "info"); dump_info(info, view); style->set_attribute("type", "text/css"); dump_user_list(user_list, users); dump_tags_style(style, tags); if(!user_list->cobj()->children) { body->remove_child(h2); body->remove_child(user_list); } style->add_child_text( ".document {\n" " border-top: 1px solid gray;\n" " border-bottom: 1px solid black;\n" " padding-bottom: 1.2em;\n" " counter-reset: line;\n" "}\n" ".line_no:before {\n" " content: counter(line);\n" " counter-increment: line;\n" "}\n" ".info {\n" " font-size: small;\n" "}\n"); style->add_child_text( uprintf( ".line_no {\n" " position: absolute;\n" " float: left;\n" " clear: left;\n" " margin-left: -%1$uem;\n" " color: gray;\n" "}\n" ".document {\n" " padding-left: %1$uem\n" "}\n", static_cast( std::log(line_counter) / std::log(10))+1)); return output.write_to_string("utf-8"); } } // anonymous namespace Gobby::OperationExportHtml::OperationExportHtml( Operations& operations, TextSessionView& view, const Glib::RefPtr& file) : Operation(operations), m_title(view.get_title()), m_file(file), m_xml(export_html(view)), m_index(0) { } Gobby::OperationExportHtml::~OperationExportHtml() { if(m_file) { // TODO: Cancel outstanding async operations? get_status_bar().remove_message(m_message_handle); } } void Gobby::OperationExportHtml::start() { m_file->replace_async( sigc::mem_fun(*this, &OperationExportHtml::on_file_replace)); m_message_handle = get_status_bar().add_info_message( Glib::ustring::compose( _("Exporting document \"%1\" to \"%2\" in HTML..."), m_title, m_file->get_uri())); } void Gobby::OperationExportHtml::on_file_replace( const Glib::RefPtr& result) { try { m_stream = m_file->replace_finish(result); m_stream->write_async( m_xml.c_str(), m_xml.length(), sigc::mem_fun( *this, &OperationExportHtml::on_stream_write)); } catch(const Glib::Exception& ex) { error(ex.what()); } } void Gobby::OperationExportHtml::on_stream_write( const Glib::RefPtr& result) { try { gssize size = m_stream->write_finish(result); // On size < 0 an exception should have been thrown. g_assert(size >= 0); m_index += size; if(m_index < m_xml.length()) { // Write next chunk m_stream->write_async( m_xml.c_str() + m_index, m_xml.length() - m_index, sigc::mem_fun( *this, &OperationExportHtml:: on_stream_write)); } else { m_stream->close(); finish(); } } catch(const Glib::Exception& ex) { error(ex.what()); } } void Gobby::OperationExportHtml::error(const Glib::ustring& message) { get_status_bar().add_error_message( Glib::ustring::compose( _("Failed to export document \"%1\" to HTML"), m_file->get_uri()), message); fail(); } code/operations/operation-export-html.hpp000066400000000000000000000035321304552132100211440ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_OPERATIONS_OPERATION_EXPORT_HTML_HPP_ #define _GOBBY_OPERATIONS_OPERATION_EXPORT_HTML_HPP_ #include "operations/operations.hpp" #include "core/documentinfostorage.hpp" #include #include #include namespace Gobby { class OperationExportHtml: public Operations::Operation, public sigc::trackable { public: OperationExportHtml(Operations& operations, TextSessionView& view, const Glib::RefPtr& file); virtual ~OperationExportHtml(); virtual void start(); protected: void on_file_replace(const Glib::RefPtr& result); void on_stream_write(const Glib::RefPtr& result); void error(const Glib::ustring& message); protected: const std::string m_title; const Glib::RefPtr m_file; const std::string m_xml; std::string::size_type m_index; Glib::RefPtr m_stream; StatusBar::MessageHandle m_message_handle; }; } #endif // _GOBBY_OPERATIONS_OPERATION_EXPORT_HTML_HPP_ code/operations/operation-new.cpp000066400000000000000000000056311304552132100174470ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "operations/operation-new.hpp" #include "core/noteplugin.hpp" #include "util/i18n.hpp" Gobby::OperationNew::OperationNew(Operations& operations, InfBrowser* browser, const InfBrowserIter* parent, const Glib::ustring& name, bool directory): Operation(operations), m_request(NULL), m_browser(browser), m_parent(*parent), m_name(name), m_directory(directory) { g_object_ref(browser); } Gobby::OperationNew::~OperationNew() { if(m_request != NULL) { g_signal_handlers_disconnect_by_func( G_OBJECT(m_request), (gpointer)G_CALLBACK(on_request_finished_static), this); g_object_unref(m_request); get_status_bar().remove_message(m_message_handle); } g_object_unref(m_browser); } void Gobby::OperationNew::start() { InfRequest* request; if(m_directory) { request = inf_browser_add_subdirectory( m_browser, &m_parent, m_name.c_str(), NULL, on_request_finished_static, this); } else { request = inf_browser_add_note( m_browser, &m_parent, m_name.c_str(), "InfText", NULL, NULL, TRUE, on_request_finished_static, this); } if(request != NULL) { m_request = request; g_object_ref(m_request); m_message_handle = get_status_bar().add_info_message( Glib::ustring::compose( m_directory ? _("Creating directory \"%1\"...") : _("Creating document \"%1\"..."), m_name)); } } void Gobby::OperationNew::on_request_finished(const InfBrowserIter* iter, const GError* error) { if(error) { const Glib::ustring prefix = Glib::ustring::compose( m_directory ? _("Failed to create directory \"%1\""): _("Failed to create document \"%1\""), m_name); get_status_bar().add_error_message(prefix, error->message); fail(); } else { if(!m_directory) { InfSessionProxy* proxy = inf_browser_get_session(m_browser, iter); g_assert(proxy != NULL); get_folder_manager().add_document( m_browser, iter, proxy, NULL); } finish(); } } code/operations/operation-new.hpp000066400000000000000000000040121304552132100174440ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_OPERATIONS_OPERATIONNEW_HPP_ #define _GOBBY_OPERATIONS_OPERATIONNEW_HPP_ #include "operations/operations.hpp" #include namespace Gobby { class OperationNew: public Operations::Operation { public: OperationNew(Operations& operations, InfBrowser* browser, const InfBrowserIter* parent, const Glib::ustring& name, bool directory); virtual ~OperationNew(); virtual void start(); protected: static void on_request_finished_static(InfRequest* request, const InfRequestResult* result, const GError* error, gpointer user_data) { const InfBrowserIter* iter; inf_request_result_get_add_node(result, NULL, NULL, &iter); static_cast(user_data)-> on_request_finished(iter, error); } void on_request_finished(const InfBrowserIter* iter, const GError* error); protected: InfBrowser* m_browser; const InfBrowserIter m_parent; Glib::ustring m_name; bool m_directory; InfRequest* m_request; StatusBar::MessageHandle m_message_handle; }; } #endif // _GOBBY_OPERATIONS_OPERATIONNEW_HPP_ code/operations/operation-open-multiple.cpp000066400000000000000000000114751304552132100214530ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ // TODO: Show "Querying file name(s)..." in statusbar when querying file // names without actually opening a file because of waiting for the file name. #include "operations/operation-open-multiple.hpp" #include "operations/operation-open.hpp" #include "util/i18n.hpp" Gobby::OperationOpenMultiple::OperationOpenMultiple( Operations& operations, const Preferences& prefs, InfBrowser* browser, const InfBrowserIter* parent, const file_list& files): Operation(operations), m_preferences(prefs), m_parent(browser, parent), m_current(NULL) { m_parent.signal_node_removed().connect( sigc::mem_fun(*this, &OperationOpenMultiple::on_node_removed)); for(file_list::const_iterator iter = files.begin(); iter != files.end(); ++iter) { info_list::iterator info_iter = m_infos.insert(m_infos.end(), Info()); Info& info = *info_iter; info.file = *iter; info.encoding = NULL; /* auto-detect... */ } } void Gobby::OperationOpenMultiple::start() { for(info_list::iterator iter = m_infos.begin(); iter != m_infos.end(); ++iter) { query(iter); } } void Gobby::OperationOpenMultiple::query(const info_list::iterator& info) { if(info->name.empty()) { try { // Query file name info->file->query_info_async( sigc::bind( sigc::mem_fun( *this, &OperationOpenMultiple:: on_query_info), info), G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME); } catch(const Gio::Error& ex) { single_error(info, ex.what()); } } else { if(!m_current) load_info(info); } } void Gobby::OperationOpenMultiple::on_node_removed() { fatal_error(_("Parent folder was removed")); } void Gobby::OperationOpenMultiple::on_query_info( const Glib::RefPtr& result, const info_list::iterator& info) { try { Glib::RefPtr file_info = info->file->query_info_finish(result); info->name = file_info->get_display_name(); if(!m_current) load_info(info); } catch(const Gio::Error& ex) { single_error(info, ex.what()); } } void Gobby::OperationOpenMultiple::on_finished( bool success, const info_list::iterator& info) { m_infos.erase(info); m_current = NULL; if(m_infos.empty()) { // All documents loaded finish(); } else { // Find the next info with name set for(info_list::iterator iter = m_infos.begin(); iter != m_infos.end(); ++iter) { if(!iter->name.empty()) { load_info(iter); break; } } // If no info was found, then wait for names to become // available, by query info results } } void Gobby::OperationOpenMultiple::load_info(const info_list::iterator& iter) { g_assert(m_current == NULL); g_assert(!iter->name.empty()); m_current = m_operations.create_document( m_parent.get_browser(), m_parent.get_browser_iter(), iter->name, m_preferences, iter->file, iter->encoding); // TODO: In principle this could be NULL if the whole operation // finished synchrounously. In this case with the current API we // cannot find out whether the operation was successful or not. What // we do does not depend on whether the operation is successful, // so it does not matter at this point. But in principle we should // change the API so that we can find it out here. Note also that // currently, OperationOpen can never finish synchrounously. if(m_current == NULL) { on_finished(true, iter); } else { m_current->signal_finished().connect( sigc::bind( sigc::mem_fun( *this, &OperationOpenMultiple::on_finished), iter)); } } void Gobby::OperationOpenMultiple::single_error( const info_list::iterator& iter, const Glib::ustring& message) { get_status_bar().add_error_message( Glib::ustring::compose( _("Failed to open document \"%1\""), iter->file->get_uri()), message); m_infos.erase(iter); // Finish operation if there are no more files to load if(m_infos.empty()) finish(); } void Gobby::OperationOpenMultiple::fatal_error(const Glib::ustring& message) { get_status_bar().add_error_message( _("Failed to open multiple documents"), message); fail(); } code/operations/operation-open-multiple.hpp000066400000000000000000000043651304552132100214600ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_OPERATIONS_OPERATION_OPEN_MULTIPLE_HPP_ #define _GOBBY_OPERATIONS_OPERATION_OPEN_MULTIPLE_HPP_ #include "operations/operations.hpp" #include "core/nodewatch.hpp" #include #include namespace Gobby { class OperationOpenMultiple: public Operations::Operation, public sigc::trackable { public: typedef Operations::file_list file_list; OperationOpenMultiple(Operations& operations, const Preferences& preferences, InfBrowser* browser, const InfBrowserIter* parent, const file_list& files); virtual void start(); protected: struct Info { Glib::RefPtr file; std::string name; const char* encoding; }; typedef std::list info_list; void query(const info_list::iterator& info); void on_node_removed(); void on_query_info(const Glib::RefPtr& result, const info_list::iterator& info); void on_finished(bool success, const info_list::iterator& info); void load_info(const info_list::iterator& iter); void single_error(const info_list::iterator& iter, const Glib::ustring& message); void fatal_error(const Glib::ustring& message); const Preferences& m_preferences; NodeWatch m_parent; unsigned int m_num_uris; info_list m_infos; OperationOpen* m_current; }; } #endif // _GOBBY_OPERATIONS_OPERATION_OPEN_MULTIPLE_HPP_ code/operations/operation-open.cpp000066400000000000000000000307461304552132100176240ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "operations/operation-open.hpp" #include "core/noteplugin.hpp" #include "util/i18n.hpp" #include #include #include #include #include // memmove. Is there some C++ replacement for this? namespace { // These are the charsets that we try to convert a file from when // autodetecting the encoding. const char* get_autodetect_encoding(unsigned int index) { // Translators: This is the 8 bit encoding that is tried when // autodetecting a file's encoding. static const char* DEFAULT_8BIT_ENCODING = N_("ISO-8859-1"); static const char* ENCODINGS[] = { "UTF-8", DEFAULT_8BIT_ENCODING, "UTF-16", "UCS-2", "UCS-4" }; static const unsigned int N_ENCODINGS = sizeof(ENCODINGS)/sizeof(ENCODINGS[0]); if(index == 1) return Gobby::_(ENCODINGS[index]); if(index < N_ENCODINGS) return ENCODINGS[index]; return NULL; } } Gobby::OperationOpen::OperationOpen(Operations& operations, const Preferences& preferences, InfBrowser* browser, const InfBrowserIter* parent, const std::string& name, const Glib::RefPtr& file, const char* encoding): Operation(operations), m_preferences(preferences), m_name(name), m_file(file), m_parent(browser, parent), m_encoding_auto_detect_index(-1), m_eol_style(DocumentInfoStorage::EOL_CR), m_request(NULL), m_raw_pos(0), m_content(NULL), m_message_handle(get_status_bar().invalid_handle()) { if(encoding != NULL) { m_encoding = encoding; } else { m_encoding_auto_detect_index = 0; m_encoding = get_autodetect_encoding(0); } } Gobby::OperationOpen::~OperationOpen() { // TODO: Cancel outstanding async operations? if(m_request != NULL) { g_signal_handlers_disconnect_by_func( G_OBJECT(m_request), (gpointer)G_CALLBACK(on_request_finished_static), this); g_object_unref(m_request); } if(m_content != NULL) g_object_unref(m_content); if(m_message_handle != get_status_bar().invalid_handle()) get_status_bar().remove_message(m_message_handle); } void Gobby::OperationOpen::start() { m_iconv.reset(new Glib::IConv("UTF-8", m_encoding)); try { m_file->read_async(sigc::mem_fun( *this, &OperationOpen::on_file_read)); m_message_handle = get_status_bar().add_info_message( Glib::ustring::compose( _("Opening document \"%1\"..."), m_file->get_uri())); m_parent.signal_node_removed().connect( sigc::mem_fun( *this, &OperationOpen::on_node_removed)); m_content = GTK_TEXT_BUFFER(gtk_source_buffer_new(NULL)); } catch(const Gio::Error& err) { error(err.what()); } } void Gobby::OperationOpen::on_node_removed() { error(_("The directory into which the new document " "was supposed to be inserted has been removed")); } void Gobby::OperationOpen::on_file_read( const Glib::RefPtr& result) { try { m_stream = m_file->read_finish(result); m_buffer.reset(new buffer); m_stream->read_async( m_buffer->buf, buffer::SIZE, sigc::mem_fun(*this, &OperationOpen::on_stream_read)); } catch(const Glib::Exception& ex) { error(ex.what()); } } void Gobby::OperationOpen::on_stream_read( const Glib::RefPtr& result) { try { gssize size = m_stream->read_finish(result); // Close stream after reading, this signals the idle handler // that all data has been read from the file. if(size <= 0) { m_stream->close(); m_stream.reset(); m_buffer.reset(NULL); // If the idle handler is not connected, then we have // already processed all the data. if(!m_idle_connection.connected()) read_finish(); } else { m_raw_content.insert(m_raw_content.end(), m_buffer->buf, m_buffer->buf + size); // Process read data in an idle handler if(!m_idle_connection.connected()) { m_idle_connection = Glib::signal_idle().connect( sigc::mem_fun(*this, &OperationOpen:: on_idle)); } m_stream->read_async( m_buffer->buf, buffer::SIZE, sigc::mem_fun( *this, &OperationOpen::on_stream_read)); } } catch(const Glib::Exception& ex) { error(ex.what()); } } bool Gobby::OperationOpen::on_idle() { static const unsigned int CONVERT_BUFFER_SIZE = 1024; const char* inbuffer = &m_raw_content[m_raw_pos]; char* inbuf = const_cast(inbuffer); gsize inbytes = m_raw_content.size() - m_raw_pos; char outbuffer[CONVERT_BUFFER_SIZE]; gchar* outbuf = outbuffer; gsize outbytes = CONVERT_BUFFER_SIZE; /* iconv is defined as libiconv on Windows, or at least when using the * binary packages from ftp.gnome.org. Therefore we can't propely * call Glib::IConv::iconv. Therefore, we use the C API here. */ const std::size_t result = g_iconv(m_iconv->gobj(), &inbuf, &inbytes, &outbuf, &outbytes); bool more_to_process = (inbytes != 0); if(result == static_cast(-1)) { if(errno == EILSEQ) { // Invalid text for the current encoding encoding_error(); return false; } if(errno == EINVAL) { // If EINVAL is set, this means that an incomplete // multibyte sequence was at the end of the input. // We might have some more bytes, but those do not // make up a whole character, so we need to wait for // more input. if(!m_stream) { // However, if we already read all input, then // there is no more input to come. We // consider this an error since the file // should not end with an incomplete multibyte // sequence. encoding_error(); return false; } else { // Otherwise, we need to wait for more data // to process. more_to_process = false; } } } m_raw_pos += (inbuf - inbuffer); // We now have outbuf - outbuffer bytes of valid UTF-8 in outbuffer. char* prev = outbuffer; char* pos; const char to_find[] = { '\r', '\n', '\0' }; /* TODO: Write directly into the buffer here, * instead of memmoving stuff. */ while( (pos = std::find_first_of(prev, outbuf, to_find, to_find + sizeof(to_find))) != outbuf) { if(*pos == '\0') { // There is a nullbyte in the conversion. As normal // text files don't contain nullbytes, this only // occurs when converting for example a UTF-16 from // ISO-8859-1 to UTF-8 (note that the UTF-16 file is // valid ISO-8859-1, it just contains lots of // nullbytes). We therefore produce an error here. encoding_error(); return false; } else { // We convert everything to '\n' as line separator, // but remember the current eol-style to correctly // save the document back to disk. prev = pos + 1; if(*pos == '\r' && prev != outbuf && *prev == '\n') { // CRLF style line break std::memmove(prev, prev + 1, outbuf - prev - 1); m_eol_style = DocumentInfoStorage::EOL_CRLF; --outbuf; } else if(*pos == '\r') { *pos = '\n'; m_eol_style = DocumentInfoStorage::EOL_CR; } else { m_eol_style = DocumentInfoStorage::EOL_LF; } } } GtkTextIter insert_iter; gtk_text_buffer_get_end_iter(m_content, &insert_iter); gtk_text_buffer_insert(m_content, &insert_iter, outbuffer, outbuf - outbuffer); // Done reading and converting the whole file if(!more_to_process && !m_stream) read_finish(); return more_to_process; } void Gobby::OperationOpen::encoding_error() { if(m_encoding_auto_detect_index == -1) { error(_("The file contains data not in the " "specified encoding")); } else { ++ m_encoding_auto_detect_index; const char* next_encoding = get_autodetect_encoding( m_encoding_auto_detect_index); if(next_encoding == NULL) { error(_("The file either contains data in an unknown " "encoding, or it contains binary data.")); } else { // Delete current content: GtkTextIter start_iter, end_iter; gtk_text_buffer_get_start_iter(m_content, &start_iter); gtk_text_buffer_get_end_iter(m_content, &end_iter); gtk_text_buffer_delete(m_content, &start_iter, &end_iter); m_raw_pos = 0; m_encoding = next_encoding; m_iconv.reset(new Glib::IConv("UTf-8", m_encoding)); // Read again, try with next encoding. m_idle_connection = Glib::signal_idle().connect( sigc::mem_fun(*this, &OperationOpen::on_idle)); } } } void Gobby::OperationOpen::read_finish() { // If the last character is a newline character, remove it. GtkTextIter end_iter, test_iter; gtk_text_buffer_get_end_iter(m_content, &end_iter); test_iter = end_iter; if(gtk_text_iter_backward_char(&test_iter)) { if(gtk_text_iter_get_char(&test_iter) == '\n') { gtk_text_buffer_delete( m_content, &test_iter, &end_iter); } } gtk_text_buffer_set_modified(m_content, FALSE); GtkTextIter insert_iter; GtkTextMark* insert = gtk_text_buffer_get_insert(m_content); gtk_text_buffer_get_iter_at_mark(m_content, &insert_iter, insert); InfUser* user = INF_USER(g_object_new( INF_TEXT_TYPE_USER, "id", 1, "flags", INF_USER_LOCAL, "name", m_preferences.user.name.get().c_str(), /* The user is made active when the user * switches to the document. */ "status", INF_USER_INACTIVE, "hue", m_preferences.user.hue.get(), "caret-position", gtk_text_iter_get_offset(&insert_iter), static_cast(NULL))); InfUserTable* user_table = inf_user_table_new(); inf_user_table_add_user(user_table, user); g_object_unref(user); InfTextGtkBuffer* text_gtk_buffer = inf_text_gtk_buffer_new(m_content, user_table); g_object_unref(user_table); ConnectionManager& connection_manager = get_browser().get_connection_manager(); InfCommunicationManager* communication_manager = connection_manager.get_communication_manager(); InfBrowser* browser = m_parent.get_browser(); InfIo* io; g_object_get(G_OBJECT(browser), "io", &io, NULL); InfTextSession* session = inf_text_session_new_with_user_table( communication_manager, INF_TEXT_BUFFER(text_gtk_buffer), io, user_table, INF_SESSION_RUNNING, NULL, NULL); g_object_unref(io); g_object_unref(text_gtk_buffer); InfRequest* request = inf_browser_add_note( m_parent.get_browser(), m_parent.get_browser_iter(), m_name.c_str(), "InfText", NULL, INF_SESSION(session), TRUE, on_request_finished_static, this); g_object_unref(session); if(request != NULL) { m_request = request; g_object_ref(m_request); // TODO: We can remove the node watch here, but need to have // the browser available in on_request_finished then. Maybe // just disconnect the signal, or bind it. } } void Gobby::OperationOpen::on_request_finished(const InfBrowserIter* iter, const GError* error) { if(error != NULL) { OperationOpen::error(error->message); } else { // Store document info so that we know where we loaded the // file from, so we don't have to ask the user where to store // it when s/he wants to save it again. DocumentInfoStorage::Info info; info.uri = m_file->get_uri(); info.encoding = m_encoding; info.eol_style = m_eol_style; get_info_storage().set_info( m_parent.get_browser(), iter, info); InfSessionProxy* proxy = inf_browser_get_session(m_parent.get_browser(), iter); g_assert(proxy != NULL); get_folder_manager().add_document( m_parent.get_browser(), iter, proxy, NULL); finish(); } } void Gobby::OperationOpen::error(const Glib::ustring& message) { get_status_bar().add_error_message( Glib::ustring::compose( _("Failed to open document \"%1\""), m_file->get_uri()), message); fail(); } code/operations/operation-open.hpp000066400000000000000000000060351304552132100176230ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_OPERATIONS_OPERATION_OPEN_HPP_ #define _GOBBY_OPERATIONS_OPERATION_OPEN_HPP_ #include "operations/operations.hpp" #include "core/documentinfostorage.hpp" #include "core/nodewatch.hpp" #include #include #include #include namespace Gobby { class OperationOpen: public Operations::Operation, public sigc::trackable { public: OperationOpen(Operations& operations, const Preferences& preferences, InfBrowser* browser, const InfBrowserIter* parent, const std::string& name, const Glib::RefPtr& file, const char* encoding /* NULL means auto-detect */); virtual ~OperationOpen(); virtual void start(); protected: static void on_request_finished_static(InfRequest* request, const InfRequestResult* result, const GError* error, gpointer user_data) { const InfBrowserIter* iter; inf_request_result_get_add_node(result, NULL, NULL, &iter); static_cast(user_data)-> on_request_finished(iter, error); } void on_node_removed(); void on_file_read(const Glib::RefPtr& result); void on_stream_read(const Glib::RefPtr& result); bool on_idle(); void encoding_error(); void read_finish(); void on_request_finished(const InfBrowserIter* iter, const GError* error); void error(const Glib::ustring& message); protected: const Preferences& m_preferences; const std::string m_name; const Glib::RefPtr m_file; NodeWatch m_parent; int m_encoding_auto_detect_index; std::unique_ptr m_iconv; std::string m_encoding; DocumentInfoStorage::EolStyle m_eol_style; struct buffer { static const unsigned int SIZE = 1024; char buf[SIZE]; }; Glib::RefPtr m_stream; std::unique_ptr m_buffer; sigc::connection m_idle_connection; InfRequest* m_request; std::vector m_raw_content; std::vector::size_type m_raw_pos; GtkTextBuffer* m_content; StatusBar::MessageHandle m_message_handle; }; } #endif // _GOBBY_OPERATIONS_OPERATION_OPEN_HPP_ code/operations/operation-save.cpp000066400000000000000000000160761304552132100176210ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "operations/operation-save.hpp" #include "util/i18n.hpp" #include #include Gobby::OperationSave::OperationSave(Operations& operations, TextSessionView& view, const Glib::RefPtr& file, const std::string& encoding, DocumentInfoStorage::EolStyle eol_style): Operation(operations), m_file(file), m_view(&view), m_start_time(std::time(NULL)), m_current_line_index(0), m_encoding(encoding), m_eol_style(eol_style), m_storage_key(view.get_info_storage_key()), m_iconv(encoding.c_str(), "UTF-8"), m_buffer_size(0), m_buffer_index(0) { const Folder& folder = get_folder_manager().get_text_folder(); folder.signal_document_removed().connect( sigc::mem_fun(*this, &OperationSave::on_document_removed)); // Load content so that the session can go on while saving GtkTextBuffer* buffer = GTK_TEXT_BUFFER(view.get_text_buffer()); GtkTextIter prev; GtkTextIter pos; GtkTextIter old_pos; gtk_text_buffer_get_start_iter(buffer, &prev); pos = prev; if(!gtk_text_iter_ends_line(&pos)) gtk_text_iter_forward_to_line_end(&pos); do { Line line; line.first = gtk_text_buffer_get_text(buffer, &prev, &pos, TRUE); line.second = gtk_text_iter_get_line_index(&pos); m_lines.push_back(line); //if(gtk_text_iter_is_end(&prev)) // break; old_pos = pos; gtk_text_iter_forward_line(&prev); gtk_text_iter_forward_to_line_end(&pos); } while(!gtk_text_iter_equal(&pos, &old_pos)); m_current_line = m_lines.begin(); } Gobby::OperationSave::~OperationSave() { // TODO: Cancel outstanding async operations? for(std::list::iterator iter = m_lines.begin(); iter != m_lines.end(); ++ iter) { g_free(iter->first); } get_status_bar().remove_message(m_message_handle); } void Gobby::OperationSave::start() { m_file->replace_async(sigc::mem_fun(*this, &OperationSave::on_file_replace)); m_message_handle = get_status_bar().add_info_message( Glib::ustring::compose( _("Saving document \"%1\" to \"%2\"..."), m_view->get_title(), m_file->get_uri())); } void Gobby::OperationSave::on_document_removed(SessionView& view) { // We keep the document to unset the modified flag when the operation // is complete, however, if the document is removed in the meanwhile, // then we don't need to care anymore. if(m_view == &view) m_view = NULL; } void Gobby::OperationSave::on_file_replace( const Glib::RefPtr& result) { try { m_stream = m_file->replace_finish(result); attempt_next(); } catch(const Glib::Exception& ex) { error(ex.what()); } } void Gobby::OperationSave::attempt_next() { bool done; if(m_current_line == m_lines.end()) { done = true; } else { // Always save a newline at the end, except the document // is empty. assert(!m_lines.empty()); std::list::const_iterator iter = m_lines.begin(); ++iter; if(iter == m_lines.end() && m_lines.front().first[0] == '\0') done = true; else done = false; } if(done) { DocumentInfoStorage::Info info; info.uri = m_file->get_uri(); info.encoding = m_encoding; info.eol_style = m_eol_style; get_info_storage().set_info(m_storage_key, info); m_stream->close(); if(m_view != NULL) { // TODO: Don't unset modified flag if the document has // changed in the meanwhile, but set // buffer-modified-time in algorithm. gtk_text_buffer_set_modified( GTK_TEXT_BUFFER(m_view->get_text_buffer()), FALSE); } finish(); } else { write_next(); } } void Gobby::OperationSave::write_next() { gchar* inbuf; gsize inlen; char newlinebuf[2] = { '\r', '\n' }; if(m_current_line_index < m_current_line->second) { inbuf = m_current_line->first + m_current_line_index; inlen = m_current_line->second - m_current_line_index; } else { // Write newline switch(m_eol_style) { case DocumentInfoStorage::EOL_CR: inbuf = newlinebuf + 0; inlen = 1; break; case DocumentInfoStorage::EOL_LF: inbuf = newlinebuf + 1; inlen = 1; break; case DocumentInfoStorage::EOL_CRLF: inbuf = newlinebuf + 0; inlen = 2; break; default: g_assert_not_reached(); break; } } gchar* outbuf = m_buffer; gsize outlen = BUFFER_SIZE; gchar* preserve_inbuf = inbuf; /* iconv is defined as libiconv on Windows, or at least when using the * binary packages from ftp.gnome.org. Therefore we can't properly * call Glib::IConv::iconv. Therefore, we use the C API here. */ std::size_t retval = g_iconv( m_iconv.gobj(), &inbuf, &inlen, &outbuf, &outlen); if(retval == static_cast(-1)) { g_assert(errno != EILSEQ); // E2BIG and EINVAL are fully OK here. } else if(retval > 0) { error(_("The document contains one or more characters that " "cannot be encoded in the specified character " "coding.")); return; } // Advance bytes read. m_current_line_index += inbuf - preserve_inbuf; m_buffer_size = BUFFER_SIZE - outlen; m_buffer_index = 0; g_assert(m_buffer_size > 0); if(m_current_line_index > m_current_line->second) { // Converted whole line: g_free(m_current_line->first); m_current_line = m_lines.erase(m_current_line); m_current_line_index = 0; } m_stream->write_async(m_buffer, m_buffer_size, sigc::mem_fun(*this, &OperationSave::on_stream_write)); } void Gobby::OperationSave::on_stream_write( const Glib::RefPtr& result) { try { gssize size = m_stream->write_finish(result); // On size < 0 an exception should have been thrown. g_assert(size >= 0); m_buffer_index += size; if(m_buffer_index < m_buffer_size) { // Write next chunk m_stream->write_async( m_buffer + m_buffer_index, m_buffer_size - m_buffer_index, sigc::mem_fun( *this, &OperationSave::on_stream_write)); } else { // Go on with next part of line and/or next line attempt_next(); } } catch(const Glib::Exception& ex) { error(ex.what()); } } void Gobby::OperationSave::error(const Glib::ustring& message) { get_status_bar().add_error_message( Glib::ustring::compose(_("Failed to save document \"%1\""), m_file->get_uri()), message); fail(); } code/operations/operation-save.hpp000066400000000000000000000053171304552132100176220ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_OPERATIONS_OPERATION_SAVE_HPP_ #define _GOBBY_OPERATIONS_OPERATION_SAVE_HPP_ #include "operations/operations.hpp" #include "core/documentinfostorage.hpp" #include #include #include #include namespace Gobby { class OperationSave: public Operations::Operation, public sigc::trackable { public: // TODO: This should maybe just take a text buffer to save, not a // textsessionview. OperationSave(Operations& operations, TextSessionView& view, const Glib::RefPtr& file, const std::string& encoding, DocumentInfoStorage::EolStyle eol_style); virtual ~OperationSave(); virtual void start(); // Note these can return NULL in case the view has been closed // in the meanwhile. TextSessionView* get_view() { return m_view; } const TextSessionView* get_view() const { return m_view; } std::time_t get_start_time() const { return m_start_time; } protected: void on_document_removed(SessionView& view); void on_file_replace(const Glib::RefPtr& result); void on_stream_write(const Glib::RefPtr& result); void attempt_next(); void write_next(); void error(const Glib::ustring& message); protected: const Glib::RefPtr m_file; TextSessionView* m_view; std::time_t m_start_time; typedef std::pair Line; std::list m_lines; std::list::iterator m_current_line; std::size_t m_current_line_index; std::string m_encoding; DocumentInfoStorage::EolStyle m_eol_style; std::string m_storage_key; Glib::IConv m_iconv; static const std::size_t BUFFER_SIZE = 1024; char m_buffer[BUFFER_SIZE]; std::size_t m_buffer_size; std::size_t m_buffer_index; Glib::RefPtr m_stream; StatusBar::MessageHandle m_message_handle; }; } #endif // _GOBBY_OPERATIONS_OPERATION_SAVE_HPP_ code/operations/operation-subscribe-path.cpp000066400000000000000000000255301304552132100215710ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "operations/operation-subscribe-path.hpp" #include "util/i18n.hpp" #include "util/uri.hpp" namespace { std::vector split_path(const std::string& path) { std::vector result; if(path.empty()) return result; if(path[0] != '/') { throw std::runtime_error( Glib::ustring::compose( Gobby::_("Invalid path: \"%1\""), path)); } std::string::size_type prev = 1, pos; while( (pos = path.find('/', prev)) != std::string::npos) { std::string component = path.substr(prev, pos - prev); if(component.empty()) { throw std::runtime_error( Glib::ustring::compose( Gobby::_("Invalid path " "component: \"%1\""), component)); } result.push_back(component); prev = pos + 1; } // Trailing '/' is allowed std::string component = path.substr(prev); if(!component.empty()) result.push_back(component); return result; } std::string make_path_string(const std::vector& path) { std::string result; for(std::vector::const_iterator iter = path.begin(); iter != path.end(); ++iter) { result += "/"; result += *iter; } return result; } } Gobby::OperationSubscribePath::OperationSubscribePath(Operations& operations, const std::string& uri): Operation(operations), m_browser(NULL), m_target(uri), m_request(NULL), m_notify_status_id(0), m_message_handle(get_status_bar().invalid_handle()) { } Gobby::OperationSubscribePath::OperationSubscribePath(Operations& operations, InfBrowser* inf_browser, const std::string& p): Operation(operations), m_browser(inf_browser), m_target(p), m_request(NULL), m_notify_status_id(0), m_message_handle(get_status_bar().invalid_handle()) { g_object_weak_ref(G_OBJECT(m_browser), on_browser_deleted_static, this); } Gobby::OperationSubscribePath::~OperationSubscribePath() { if(m_request != NULL) { g_signal_handlers_disconnect_by_func( G_OBJECT(m_request), (gpointer)G_CALLBACK(on_subscribe_finished_static), this); g_signal_handlers_disconnect_by_func( G_OBJECT(m_request), (gpointer)G_CALLBACK(on_explore_finished_static), this); } if(m_notify_status_id != 0) g_signal_handler_disconnect(m_browser, m_notify_status_id); if(m_message_handle != get_status_bar().invalid_handle()) get_status_bar().remove_message(m_message_handle); if(m_browser != NULL) { g_object_weak_unref(G_OBJECT(m_browser), on_browser_deleted_static, this); } } void Gobby::OperationSubscribePath::start() { try { if(m_browser) { m_path = split_path(m_target); m_message_handle = get_status_bar().add_info_message( Glib::ustring::compose( _("Subscribing to \"%1\"..."), m_target)); start_with_browser(); } else { start_without_browser(); } } catch(const std::exception& ex) { get_status_bar().add_error_message( Glib::ustring::compose( _("Failed to connect to \"%1\""), m_target), ex.what()); fail(); } } void Gobby::OperationSubscribePath::start_without_browser() { std::string scheme, netloc, path; parse_uri(m_target, scheme, netloc, path); if(scheme != "infinote") { throw std::runtime_error( Glib::ustring::compose( _("URI scheme \"%1\" not supported"), scheme)); } m_path = split_path(path); std::string hostname, service; unsigned int device_index; parse_netloc(netloc, hostname, service, device_index); try { m_browser = get_browser().add_remote( hostname, service, device_index, true); g_assert(m_browser != NULL); // We need a weak reference on the browser, so that we can // cancel the operation in case it disappears. This happens // when the connection manager notices that a connection to // the host which the new connection is trying to connect to // exists already. g_object_weak_ref(G_OBJECT(m_browser), on_browser_deleted_static, this); if(m_path.empty()) { m_message_handle = get_status_bar().add_info_message( Glib::ustring::compose( _("Connecting to \"%1\"..."), m_target)); } else { m_message_handle = get_status_bar().add_info_message( Glib::ustring::compose( _("Subscribing to \"%1\"..."), m_target)); } start_with_browser(); } catch(const std::exception& ex) { if(m_path.empty()) { get_status_bar().add_error_message( Glib::ustring::compose( _("Failed to connect to \"%1\""), m_target), ex.what()); } else { get_status_bar().add_error_message( Glib::ustring::compose( _("Could not subscribe to \"%1\""), m_target), ex.what()); } fail(); } } void Gobby::OperationSubscribePath::start_with_browser() { InfBrowserStatus status; g_object_get(G_OBJECT(m_browser), "status", &status, NULL); if(status == INF_BROWSER_OPEN) { inf_browser_get_root(m_browser, &m_path_iter); m_path_index = 0; explore(); } else { m_notify_status_id = g_signal_connect( G_OBJECT(m_browser), "notify::status", G_CALLBACK(on_notify_status_static), this); } } void Gobby::OperationSubscribePath::explore() { if(m_path_index == m_path.size()) { // We are done get_browser().set_selected(m_browser, &m_path_iter); if(inf_browser_is_subdirectory(m_browser, &m_path_iter)) { finish(); } else { InfSessionProxy* proxy = inf_browser_get_session( m_browser, &m_path_iter); if(proxy != NULL) { InfSession* session; g_object_get(G_OBJECT(proxy), "session", &session, NULL); SessionView* view = get_folder_manager().lookup_document( session); g_object_unref(session); if(view != NULL) { get_folder_manager(). switch_to_document( *view); } else { get_folder_manager().add_document( m_browser, &m_path_iter, proxy, NULL); } finish(); } else { make_subscribe_request(); } } } else { if(inf_browser_is_subdirectory(m_browser, &m_path_iter)) { // This is a subdirectory node. Explore it. if(inf_browser_get_explored(m_browser, &m_path_iter)) { descend(); } else { make_explore_request(); } } else { // This is a leaf node. This is an error, since we // did not yet end up at the end of the path. get_browser().set_selected(m_browser, &m_path_iter); get_status_bar().remove_message(m_message_handle); get_status_bar().add_error_message( Glib::ustring::compose( _("Could not subscribe to \"%1\""), m_target), Glib::ustring::compose( _("Path \"%1\" does not exist"), make_path_string(m_path))); fail(); } } } void Gobby::OperationSubscribePath::descend() { g_assert(m_path_index < m_path.size()); g_assert(inf_browser_is_subdirectory(m_browser, &m_path_iter)); g_assert(inf_browser_get_explored(m_browser, &m_path_iter)); if(inf_browser_get_child(m_browser, &m_path_iter)) { do { const char* name = inf_browser_get_node_name( m_browser, &m_path_iter); if(m_path[m_path_index] == name) { ++m_path_index; explore(); return; } } while(inf_browser_get_next(m_browser, &m_path_iter)); } // Cannot find child get_status_bar().remove_message(m_message_handle); m_message_handle = get_status_bar().invalid_handle(); get_status_bar().add_error_message( Glib::ustring::compose( _("Could not subscribe to \"%1\""), m_target), Glib::ustring::compose( _("Path \"%1\" does not exist"), make_path_string(m_path))); fail(); } void Gobby::OperationSubscribePath::make_explore_request() { g_assert(m_request == NULL); m_request = inf_browser_get_pending_request( m_browser, &m_path_iter, "explore-node"); if(m_request == NULL) { m_request = inf_browser_explore( m_browser, &m_path_iter, on_explore_finished_static, this); } else { g_signal_connect( G_OBJECT(m_request), "finished", G_CALLBACK(on_explore_finished_static), this); } } void Gobby::OperationSubscribePath::make_subscribe_request() { g_assert(m_request == NULL); m_request = inf_browser_get_pending_request( m_browser, &m_path_iter, "subscribe-session"); if(m_request == NULL) { m_request = inf_browser_subscribe( m_browser, &m_path_iter, on_subscribe_finished_static, this); } else { g_signal_connect( G_OBJECT(m_request), "finished", G_CALLBACK(on_subscribe_finished_static), this); } } void Gobby::OperationSubscribePath::on_notify_status() { InfBrowserStatus status; g_object_get(G_OBJECT(m_browser), "status", &status, NULL); switch(status) { case INF_BROWSER_OPEN: g_signal_handler_disconnect(m_browser, m_notify_status_id); m_notify_status_id = 0; inf_browser_get_root(m_browser, &m_path_iter); m_path_index = 0; explore(); break; case INF_BROWSER_OPENING: break; case INF_BROWSER_CLOSED: // Don't set an error message, the user will already be // notified by the closed browser. fail(); break; default: g_assert_not_reached(); break; } } void Gobby::OperationSubscribePath::on_browser_deleted() { m_browser = NULL; m_notify_status_id = 0; // Don't set an error message, the user will already be // notified by the closed browser. fail(); } void Gobby::OperationSubscribePath::on_explore_finished(const GError* error) { m_request = NULL; if(error != NULL) { get_status_bar().add_error_message( Glib::ustring::compose( _("Could not subscribe to \"%1\""), m_target), error->message); fail(); } else { descend(); } } void Gobby::OperationSubscribePath::on_subscribe_finished( const InfBrowserIter* iter, const GError* error) { m_request = NULL; if(error != NULL) { get_status_bar().add_error_message( Glib::ustring::compose( _("Could not subscribe to \"%1\""), m_target), error->message); fail(); } else { InfSessionProxy* proxy = inf_browser_get_session(m_browser, iter); g_assert(proxy != NULL); get_folder_manager().add_document( m_browser, iter, proxy, NULL); /* From this point on subscription-commands takes over */ finish(); } } code/operations/operation-subscribe-path.hpp000066400000000000000000000066021304552132100215750ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_OPERATIONS_OPERATIONSUBSCRIBEPATH_HPP_ #define _GOBBY_OPERATIONS_OPERATIONSUBSCRIBEPATH_HPP_ #include "operations/operations.hpp" #include "core/browser.hpp" #include namespace Gobby { class OperationSubscribePath: public Operations::Operation { public: OperationSubscribePath(Operations& operations, const std::string& uri); OperationSubscribePath(Operations& operations, InfBrowser* browser, const std::string& path); virtual ~OperationSubscribePath(); virtual void start(); private: void start_without_browser(); void start_with_browser(); void explore(); void descend(); void make_explore_request(); void make_subscribe_request(); static void on_notify_status_static(GObject* object, GParamSpec* pspec, gpointer user_data) { static_cast(user_data)-> on_notify_status(); } static void on_browser_deleted_static(gpointer user_data, GObject* where_the_object_was) { static_cast(user_data)-> on_browser_deleted(); } static void on_explore_finished_static(InfRequest* request, const InfRequestResult* result, const GError* error, gpointer user_data) { static_cast(user_data)-> on_explore_finished(error); } static void on_subscribe_finished_static(InfRequest* request, const InfRequestResult* res, const GError* error, gpointer user_data) { const InfBrowserIter* iter; if(error == NULL) { inf_request_result_get_subscribe_session( res, NULL, &iter, NULL); } static_cast(user_data)-> on_subscribe_finished(iter, error); } void on_notify_status(); void on_browser_deleted(); void on_explore_finished(const GError* error); void on_subscribe_finished(const InfBrowserIter* iter, const GError* error); InfBrowser* m_browser; std::string m_target; std::vector m_path; std::vector::size_type m_path_index; InfBrowserIter m_path_iter; InfRequest* m_request; gulong m_notify_status_id; StatusBar::MessageHandle m_message_handle; }; } #endif // _GOBBY_OPERATIONS_OPERATIONSUBSCRIBEPATH_HPP_ code/operations/operations.cpp000066400000000000000000000140161304552132100170400ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "operations/operation-new.hpp" #include "operations/operation-open.hpp" #include "operations/operation-open-multiple.hpp" #include "operations/operation-save.hpp" #include "operations/operation-delete.hpp" #include "operations/operation-subscribe-path.hpp" #include "operations/operation-export-html.hpp" #include "operations/operations.hpp" #include "core/noteplugin.hpp" #include "util/i18n.hpp" Gobby::Operations::Operation::~Operation() {} Gobby::Operations::Operations(DocumentInfoStorage& info_storage, Browser& browser, FolderManager& folder_manager, StatusBar& status_bar): m_info_storage(info_storage), m_browser(browser), m_folder_manager(folder_manager), m_status_bar(status_bar) { } Gobby::Operations::~Operations() { for(OperationSet::iterator iter = m_operations.begin(); iter != m_operations.end(); ++ iter) { delete *iter; } } Gobby::OperationNew* Gobby::Operations::create_directory(InfBrowser* browser, const InfBrowserIter* parent, const Glib::ustring& name) { OperationNew* op = new OperationNew(*this, browser, parent, name, true); m_operations.insert(op); op->start(); return check_operation(op); } Gobby::OperationNew* Gobby::Operations::create_document(InfBrowser* browser, const InfBrowserIter* parent, const Glib::ustring& name) { OperationNew* op = new OperationNew(*this, browser, parent, name, false); m_operations.insert(op); op->start(); return check_operation(op); } Gobby::OperationOpen* Gobby::Operations::create_document(InfBrowser* browser, const InfBrowserIter* parent, const Glib::ustring& name, const Preferences& preferences, const Glib::RefPtr& file, const char* encoding) { OperationOpen* op = new OperationOpen(*this, preferences, browser, parent, name, file, encoding); m_operations.insert(op); op->start(); return check_operation(op); } Gobby::OperationOpenMultiple* Gobby::Operations::create_documents(InfBrowser* browser, const InfBrowserIter* parent, const Preferences& prefs, const file_list& files) { OperationOpenMultiple* op = new OperationOpenMultiple(*this, prefs, browser, parent, files); m_operations.insert(op); op->start(); return check_operation(op); } Gobby::OperationSave* Gobby::Operations::save_document(TextSessionView& view, const Glib::RefPtr& file, const std::string& encoding, DocumentInfoStorage::EolStyle eol_style) { OperationSave* prev_op = get_save_operation_for_document(view); // Cancel previous save operation: if(prev_op != NULL) fail_operation(prev_op); OperationSave* op = new OperationSave(*this, view, file, encoding, eol_style); m_operations.insert(op); m_signal_begin_save_operation.emit(op); op->start(); return check_operation(op); } Gobby::OperationDelete* Gobby::Operations::delete_node(InfBrowser* browser, const InfBrowserIter* iter) { OperationDelete* op = new OperationDelete(*this, browser, iter); m_operations.insert(op); op->start(); return check_operation(op); } Gobby::OperationSubscribePath* Gobby::Operations::subscribe_path(const std::string& uri) { OperationSubscribePath* op = new OperationSubscribePath(*this, uri); m_operations.insert(op); op->start(); return check_operation(op); } Gobby::OperationSubscribePath* Gobby::Operations::subscribe_path(InfBrowser* browser, const std::string& path) { OperationSubscribePath* op = new OperationSubscribePath(*this, browser, path); m_operations.insert(op); op->start(); return check_operation(op); } Gobby::OperationExportHtml* Gobby::Operations::export_html(TextSessionView& view, const Glib::RefPtr& file) { OperationExportHtml* op = new OperationExportHtml(*this, view, file); m_operations.insert(op); op->start(); return op; } Gobby::OperationSave* Gobby::Operations::get_save_operation_for_document(TextSessionView& view) { for(OperationSet::iterator iter = m_operations.begin(); iter != m_operations.end(); ++ iter) { Operation* op = *iter; OperationSave* save_op = dynamic_cast(op); if(save_op != NULL) { if(save_op->get_view() == &view) return save_op; } } return NULL; } void Gobby::Operations::finish_operation(Operation* operation) { m_operations.erase(operation); operation->signal_finished().emit(true); delete operation; } void Gobby::Operations::fail_operation(Operation* operation) { m_operations.erase(operation); operation->signal_finished().emit(false); delete operation; } code/operations/operations.hpp000066400000000000000000000117171304552132100170520ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_OPERATIONS_OPERATIONS_HPP_ #define _GOBBY_OPERATIONS_OPERATIONS_HPP_ #include "core/browser.hpp" #include "core/statusbar.hpp" #include "core/documentinfostorage.hpp" #include "core/foldermanager.hpp" #include "core/textsessionview.hpp" #include #include #include #include namespace Gobby { class OperationNew; class OperationOpen; class OperationOpenMultiple; class OperationSave; class OperationDelete; class OperationSubscribePath; class OperationExportHtml; class Operations: public sigc::trackable { public: class Operation { public: typedef sigc::signal SignalFinished; Operation(Operations& operations): m_operations(operations) {} virtual ~Operation() = 0; virtual void start() = 0; StatusBar& get_status_bar() { return m_operations.m_status_bar; } Browser& get_browser() { return m_operations.m_browser; } FolderManager& get_folder_manager() { return m_operations.m_folder_manager; } DocumentInfoStorage& get_info_storage() { return m_operations.m_info_storage; } SignalFinished signal_finished() const { return m_signal_finished; } protected: void fail() { m_operations.fail_operation(this); } void finish() { m_operations.finish_operation(this); } Operations& m_operations; private: SignalFinished m_signal_finished; }; typedef sigc::signal SignalBeginSaveOperation; typedef std::vector > file_list; Operations(DocumentInfoStorage& info_storage, Browser& browser, FolderManager& folder_manager, StatusBar& status_bar); ~Operations(); OperationNew* create_directory(InfBrowser* browser, const InfBrowserIter* parent, const Glib::ustring& name); OperationNew* create_document(InfBrowser* browser, const InfBrowserIter* parent, const Glib::ustring& name); OperationOpen* create_document(InfBrowser* browser, const InfBrowserIter* parent, const Glib::ustring& name, const Preferences& preferences, const Glib::RefPtr& file, const char* encoding); OperationOpenMultiple* create_documents(InfBrowser* browser, const InfBrowserIter* parent, const Preferences& prefs, const file_list& files); OperationSave* save_document(TextSessionView& view, const Glib::RefPtr& file, const std::string& encoding, DocumentInfoStorage::EolStyle eol_style); OperationDelete* delete_node(InfBrowser* browser, const InfBrowserIter* iter); // uri must be of kind infinote://[hostname]/[path] OperationSubscribePath* subscribe_path(const std::string& uri); OperationSubscribePath* subscribe_path(InfBrowser* browser, const std::string& path); OperationExportHtml* export_html(TextSessionView& view, const Glib::RefPtr& file); OperationSave* get_save_operation_for_document(TextSessionView& view); SignalBeginSaveOperation signal_begin_save_operation() const { return m_signal_begin_save_operation; } protected: void fail_operation(Operation* operation); void finish_operation(Operation* operation); DocumentInfoStorage& m_info_storage; Browser& m_browser; FolderManager& m_folder_manager; StatusBar& m_status_bar; typedef std::set OperationSet; OperationSet m_operations; SignalBeginSaveOperation m_signal_begin_save_operation; private: template OperationType* check_operation(OperationType* op) { if(m_operations.find(op) == m_operations.end()) return NULL; return op; } }; } #endif // _GOBBY_OPERATIONS_OPERATIONS_HPP_ code/resources/000077500000000000000000000000001304552132100137765ustar00rootroot00000000000000code/resources/gobby.gresources.xml000066400000000000000000000016511304552132100200050ustar00rootroot00000000000000 ui/browser-context-menu.ui ui/connection-dialog.ui ui/connection-info-dialog.ui ui/document-location-dialog.ui ui/entry-dialog.ui ui/find-dialog.ui ui/goto-dialog.ui ui/initial-dialog.ui ui/menu.ui ui/open-location-dialog.ui ui/password-dialog.ui ui/preferences-dialog.ui ui/toolbar.ui code/resources/ui/000077500000000000000000000000001304552132100144135ustar00rootroot00000000000000code/resources/ui/browser-context-menu.ui000066400000000000000000000034411304552132100210630ustar00rootroot00000000000000
_Remove browser.remove
_Disconnect From Server browser.disconnect _Connection Info... browser.connection-info _Create Account... browser.create-account
Create Do_cument... browser.create-document Create Director_y... browser.create-directory _Open Document... browser.open-document
_Permissions... browser.permissions D_elete browser.delete
code/resources/ui/connection-dialog.ui000066400000000000000000000101021304552132100203400ustar00rootroot00000000000000 False 12 Connect To Server False dialog False vertical 6 False end False False 0 True False 6 6 True False 6 network-server 6 0 0 2 True False Please enter a host name with which to establish a connection. 1 0 2 True False _Remote Endpoint: True entry 1 1 True True True True 2 1 False True 1 code/resources/ui/connection-info-dialog.ui000066400000000000000000000071401304552132100213010ustar00rootroot00000000000000 False 12 Connection Information False dialog False vertical 6 False end False False 0 True False 12 True False 6 GTK_ALIGN_START 0 0 True True in 200 80 True True 1 0 True 2 0 False True 1 code/resources/ui/document-location-dialog.ui000066400000000000000000000104441304552132100216360ustar00rootroot00000000000000 False 12 True 480 dialog False vertical 6 False end False False 0 True False vertical 6 6 True False Document Name: 0 0 True True True 1 0 True False start 0 1 2 True True in True True True 0 2 2 False True 1 code/resources/ui/entry-dialog.ui000066400000000000000000000052511304552132100173530ustar00rootroot00000000000000 False dialog False 12 False vertical 6 False end False False 0 True False 6 True False entry True 0 0 True True True True 1 0 False True 1 code/resources/ui/find-dialog.ui000066400000000000000000000167111304552132100171350ustar00rootroot00000000000000 False 12 False dialog False vertical 6 False end False False 0 True False 6 12 True False baseline 0 _Search For: True search-for 0 0 True True baseline True True 1 0 True False baseline 0 Replace _With: True replace-with 0 1 True True baseline True True 1 1 _Match Case True True False True 0 True 0 2 2 Match _entire word only True True False True 0 True 0 3 2 Search _backwards True True False True 0 True 0 4 2 Wra_p Around True True False True 0 True True 0 5 2 False True 1 code/resources/ui/goto-dialog.ui000066400000000000000000000057731304552132100171730ustar00rootroot00000000000000 False 12 Go To Line False dialog False vertical 6 False end False False 0 True False 6 6 True False Line _Number: True spin-button 0 0 True True True True number True 1 0 False True 1 code/resources/ui/initial-dialog.ui000066400000000000000000000533711304552132100176510ustar00rootroot00000000000000 False 12 Connect To Server False dialog False vertical 6 False end False False 0 True False vertical 6 12 True False center start Welcome to Gobby 1 0 True False 0 0 Before we start, a few options need to be configured. You can later change them by choosing <i>Preferences</i> from the application menu. True True 40 1 1 True False start start 128 gobby-0.5 6 0 0 6 True False 6 12 True False start center 6 User Name 0 0 True True 6 True True 1 0 True False start center Your name as shown to other users. 0 1 2 True False start center 6 User Color 0 2 True True True 6 True Choose a User Color 1 2 True False start center The color with which text you have written is branded. 0 3 2 1 2 True False start center 6 Remote Connections 1 3 Allow remote users to edit local documents True True False 0 True 1 4 True False 12 vertical 6 Ask remote users to enter a password True True False 0 True 0 0 True False 12 12 True False start center Password: end 0 0 True True True False 1 0 0 1 True False start center Authentication 0 2 Create a self-signed certificate (Recommended) True True False 12 0 True create-self-signed 0 3 True False start center 12 0 It may take a few seconds for the certificate to be generated. 0 4 Use an existing certificate (Expert Option) True True False 12 0 True True create-self-signed 0 5 True False start center 12 0 Use an existing private key and certificate. The files must be in PEM format. True 0 6 True False 24 6 12 True False start center Private Key: 0 0 True False start center Certificate: 0 1 True False True 1 0 True False True 1 1 0 7 1 5 False True 1 code/resources/ui/menu.ui000066400000000000000000000205341304552132100157220ustar00rootroot00000000000000
_Preferences app.preferences
Help app.help About app.about _Quit app.quit
_File
_New... win.new <primary>n _Open... win.open <primary>o Open _Location... win.open-location <primary>l
_Save win.save <primary>s Save _As... win.save-as <primary><shift>s Save All win.save-all <primary><shift>l
Export As _HTML... win.export-html <primary><shift>h Connect _To Server... win.connect <primary>t
_Close win.close <primary>w
_Edit
_Undo win.undo <primary>z _Redo win.redo <primary><shift>z
Cut win.cut <primary>x _Copy win.copy <primary>c Paste win.paste <primary>v
_Find... win.find <primary>f Find Ne_xt win.find-next <primary>g _Find Pre_vious win.find-prev <primary><shift>g Find And Re_place... win.find-replace <primary>h Go To _Line win.goto-line <primary>i
_View
Reset User Colors win.hide-user-colors <primary><shift>c
Fullscreen win.fullscreen F11 Zoom In win.zoom-in <primary>plus Zoom Out win.zoom-out <primary>minus
View Tool Bar win.view-toolbar View Status Bar win.view-statusbar
View Document Browser win.view-browser F9 View Chat win.view-chat <shift>F9 View Document User List win.view-document-user-list <primary>F9 View Chat User List win.view-chat-user-list <primary><shift>F9
_Highlight Mode None win.highlight-mode
code/resources/ui/menu.ui.h000066400000000000000000000012661304552132100161510ustar00rootroot00000000000000N_("_Preferences"); N_("Help"); N_("About"); N_("_Quit"); N_("_File"); N_("_New..."); N_("_Open..."); N_("Open _Location..."); N_("_Save"); N_("Save _As..."); N_("Save All"); N_("Export As _HTML..."); N_("Connect _To Server..."); N_("_Close"); N_("_Edit"); N_("_Undo"); N_("_Redo"); N_("Cut"); N_("_Copy"); N_("Paste"); N_("_Find..."); N_("Find Ne_xt"); N_("_Find Pre_vious"); N_("Find And Re_place..."); N_("Go To _Line"); N_("_View"); N_("Reset User Colors"); N_("Fullscreen"); N_("Zoom In"); N_("Zoom Out"); N_("View Tool Bar"); N_("View Status Bar"); N_("View Document Browser"); N_("View Chat"); N_("View Document User List"); N_("View Chat User List"); N_("_Highlight Mode"); N_("None"); code/resources/ui/open-location-dialog.ui000066400000000000000000000061631304552132100207640ustar00rootroot00000000000000 False 12 Open Location False dialog False vertical 6 False end False False 0 True False vertical 6 6 True False start 0 Enter the location (URI) of the document you would like to open. True 30 0 0 True False True True 0 1 False True 1 code/resources/ui/password-dialog.ui000066400000000000000000000103351304552132100200530ustar00rootroot00000000000000 False 12 Password Required False dialog False vertical 6 False end False False 0 True False 6 6 True False 6 network-server 6 0 0 2 True False True 30 1 0 2 True False Server _Password: True password 1 1 True True True False True password 2 1 False True 1 code/resources/ui/preferences-dialog.ui000066400000000000000000002142601304552132100205150ustar00rootroot00000000000000 False 12 Preferences False dialog False vertical 6 False end False False 0 True True True False 12 vertical 6 True False 12 6 12 True False start User _Name: True user-name 0 0 True True True 1 0 True False start User Colo_r: True user-color 0 1 True True True True 1 1 True False start Color Intensity: 0 2 True True True 4 False False 1 2 0 1 True False start Settings 0 0 True False start Remote Users 0 2 True False 12 vertical 6 Show cursors of remote users True True False True 0 True 0 0 Show selections of remote users True True False True 0 True 0 1 Highlight current line of remote users True True False True 0 True 0 2 Indicate cursor position of remote users in the scrollbar True True False True 0 True 0 3 0 3 True False start Local Documents 0 4 True False 12 6 Allow remote users to edit local documents True True False True 0 True 0 0 True False 12 vertical 6 12 Require remote users to enter a password True True False 0 True 0 0 2 True False start _TCP Port Number: True port-number 0 2 True True True 1 2 True False 12 12 True False _Password: True password-entry 0 0 True False True True 1 0 0 1 2 0 1 Remeber local documents after Gobby restart True True False True 0 True 0 2 True False 12 12 True False Local Documents _Directory: True local-documents-directory 0 0 True False True 1 0 0 3 0 5 True False User False True False 12 vertical 6 True False start Tab Stops 0 0 True False 12 6 12 True False start Tab _Width: True tab-width 0 0 True True True 1 0 Insert spaces instead of tabs True True False True 0 True 0 1 2 0 1 True False start Indentation 0 2 Enable automatic indentation True True False 12 True 0 True 0 3 True False start Home/End Behavior 0 4 Smart Home/End True True False With this option enabled, pressing home or end at the beginning or end of a line, respectively, moves the cursor to the first or last non-whitespace character in that line. 12 True 0 True 0 5 True False start File Saving 0 6 True False 12 vertical 6 Enable automatic saving of documents True True False True 0 True 0 0 True False 12 12 True False start Autosave interval in minutes: 0 0 True True True 1 0 0 1 0 7 1 True False Editor 1 False True False 12 vertical 6 True False start Text Wrapping 0 0 Enable text wrapping True True False 12 True 0 True 0 1 Do not split words over two lines True True False 24 True 0 True 0 2 True False start Line Numbers 0 3 Display line numbers True True False 12 True 0 True 0 4 True False start Current Line 0 5 Highlight current line True True False 12 True 0 True 0 6 True False start Right Margin 0 7 True False 12 vertical 6 Display right margin True True False True 0 True 0 0 True False 12 12 True False start Right _margin at column: True right-margin-column 0 0 True True True 1 0 0 1 0 8 True False start Bracket Matching 0 9 Highlight matching brackets True True False 12 True 0 True 0 10 True False start Whitespace Display 0 11 True False 12 True 0 12 2 True False View 2 False True False 12 vertical 6 True False start Toolbar 0 0 True False 12 True 0 1 True False start Font 0 2 True True True 12 True Sans 12 0 3 True False start Color Scheme 0 4 True True 12 in True True True True False 0 5 3 True False Appearance 3 False True False 12 vertical 6 True False start Trusted Certificate Authorities (CAs) 0 0 True False 12 6 12 Trust this computer's default CAs True True False True 0 True 0 0 2 True False start Additionally Trusted CAs: 0 1 True False True 1 1 False start 0 True True 0 2 2 0 1 True False start Secure Connection 0 2 True False 12 True 0 3 True False start Authentication 0 4 True False 12 vertical 6 None True True False 0 True True 0 0 Authenticate with a certificate True True False 0 True True authentication-none 0 1 True False 12 6 12 True False start Private Key: 0 0 True False start Certificate: 0 2 False start 0 True True 0 3 3 False start 0 True True 0 1 3 True False True 1 0 True False True 1 2 Create New... True True True 2 0 Create New... True True True 2 2 0 2 0 5 4 True False Security 4 False False True 1 code/resources/ui/toolbar.ui000066400000000000000000000145741304552132100164270ustar00rootroot00000000000000 False True False win.new document-new True New False True True False win.open document-open True Open False True True False win.save document-save True Save False True True False True False win.undo edit-undo True Undo False True True False win.redo edit-redo True Redo False True True False True False win.cut edit-cut True Cut False True True False win.copy edit-copy True Copy False True True False win.paste edit-paste True Paste False True True False True False win.find edit-find True Find False True True False win.find-replace edit-find-replace True Find And Replace False True code/resources/ui/toolbar.ui.h000066400000000000000000000002031304552132100166350ustar00rootroot00000000000000N_("New"); N_("Open"); N_("Save"); N_("Undo"); N_("Redo"); N_("Cut"); N_("Copy"); N_("Paste"); N_("Find"); N_("Find And Replace"); code/util/000077500000000000000000000000001304552132100127415ustar00rootroot00000000000000code/util/Makefile.am000066400000000000000000000007521304552132100150010ustar00rootroot00000000000000gobby_0_5_SOURCES += \ code/util/asyncoperation.cpp \ code/util/closebutton.cpp \ code/util/config.cpp \ code/util/file.cpp \ code/util/historyentry.cpp \ code/util/i18n.cpp \ code/util/serialize.cpp \ code/util/uri.cpp noinst_HEADERS += \ code/util/asyncoperation.hpp \ code/util/closebutton.hpp \ code/util/config.hpp \ code/util/defaultaccumulator.hpp \ code/util/file.hpp \ code/util/historyentry.hpp \ code/util/i18n.hpp \ code/util/serialize.hpp \ code/util/uri.hpp code/util/asyncoperation.cpp000066400000000000000000000045171304552132100165120ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "util/asyncoperation.hpp" #include #include #include Gobby::AsyncOperation::Handle::Handle(AsyncOperation& operation): m_operation(&operation) { } Gobby::AsyncOperation::Handle::~Handle() { if(m_operation) { if(!m_operation->m_finished) cancel(); m_operation->m_handle = NULL; } } void Gobby::AsyncOperation::Handle::cancel() { assert(m_operation); assert(m_operation->m_finished == false); m_operation->m_finished = true; } Gobby::AsyncOperation::AsyncOperation(): m_thread(NULL), m_handle(NULL), m_finished(false) { } Gobby::AsyncOperation::~AsyncOperation() { if(m_handle) m_handle->m_operation = NULL; } std::unique_ptr Gobby::AsyncOperation::start(std::unique_ptr operation) { assert(operation->m_thread == NULL); assert(operation->m_handle == NULL); assert(operation->m_finished == false); AsyncOperation* op = operation.release(); std::unique_ptr handle(new Handle(*op)); op->m_handle = handle.get(); op->m_thread = Glib::Thread::create( sigc::mem_fun(*op, &AsyncOperation::thread_run), true); return handle; } void Gobby::AsyncOperation::thread_run() { run(); Glib::signal_idle().connect( sigc::mem_fun(*this, &AsyncOperation::done)); } bool Gobby::AsyncOperation::done() { if(!m_finished) { // m_handle DTOR cancels the operation g_assert(m_handle != NULL); m_finished = true; finish(); } if(m_handle) m_handle->m_operation = NULL; delete this; return false; } code/util/asyncoperation.hpp000066400000000000000000000030671304552132100165160ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_ASYNC_OPERATION_HPP_ #define _GOBBY_ASYNC_OPERATION_HPP_ #include #include namespace Gobby { class AsyncOperation { public: class Handle { friend class AsyncOperation; public: Handle(AsyncOperation& operation); ~Handle(); void cancel(); private: AsyncOperation* m_operation; }; AsyncOperation(); virtual ~AsyncOperation(); static std::unique_ptr start(std::unique_ptr operation); protected: virtual void run() = 0; virtual void finish() = 0; const Handle* get_handle() const { return m_handle; } private: void thread_run(); bool done(); Glib::Thread* m_thread; Handle* m_handle; bool m_finished; }; } #endif // _GOBBY_ASYNC_OPERATION_HPP_ code/util/closebutton.cpp000066400000000000000000000032661304552132100160150ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "util/closebutton.hpp" #include #include Gobby::CloseButton::CloseButton() { set_relief(Gtk::RELIEF_NONE); set_focus_on_click(false); static const gchar button_style[] = "* {\n" " -GtkButton-default-border: 0;\n" " -GtkButton-default-outside-border: 0;\n" " -GtkButton-inner-border: 0;\n" " -GtkWidget-focus-line-width: 0;\n" " -GtkWidget-focus-padding: 0;\n" " padding: 0;\n" "}"; Glib::RefPtr provider = Gtk::CssProvider::create(); provider->load_from_data(button_style); get_style_context()->add_provider( provider, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); Gtk::Image* button_image = Gtk::manage(new Gtk::Image); button_image->set_from_icon_name("window-close", Gtk::ICON_SIZE_MENU); add(*button_image); button_image->show(); } code/util/closebutton.hpp000066400000000000000000000020701304552132100160120ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_CLOSEBUTTON_HPP_ #define _GOBBY_CLOSEBUTTON_HPP_ #include namespace Gobby { class CloseButton: public Gtk::Button { public: CloseButton(); }; } // namespace Gobby #endif // _GOBBY_CLOSEBUTTON_HPP_ code/util/config.cpp000066400000000000000000000144601304552132100147170ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "util/config.hpp" #include "util/file.hpp" #include "util/i18n.hpp" #include #include #include #include #include #include namespace { template typename Map::mapped_type ptrmap_find(const Map& map, const typename Map::key_type& key) { typename Map::const_iterator iter = map.find(key); if(iter == map.end() ) return NULL; return iter->second; } } Gobby::Config::Entry::Entry(const Glib::ustring& name): m_name(name) { } const Glib::ustring& Gobby::Config::Entry::get_name() const { return m_name; } Gobby::Config::ParentEntry::ParentEntry(const Glib::ustring& name): Entry(name) { } Gobby::Config::ParentEntry::ParentEntry(const xmlpp::Element& elem): Entry(elem.get_name() ) { xmlpp::Node::NodeList list = elem.get_children(); for(xmlpp::Node::NodeList::iterator iter = list.begin(); iter != list.end(); ++ iter) { xmlpp::Element* child = dynamic_cast(*iter); if(child == NULL) continue; if(child->get_child_text() && !child->get_child_text()->is_white_space()) { ValueEntry* entry = new TypedValueEntry( *child ); m_map[child->get_name()] = entry; } else { m_map[child->get_name()] = new ParentEntry(*child); } } } Gobby::Config::ParentEntry::~ParentEntry() { for(map_type::iterator iter = m_map.begin(); iter != m_map.end(); ++ iter) { delete iter->second; } } void Gobby::Config::ParentEntry::save(xmlpp::Element& elem) const { for(map_type::const_iterator iter = m_map.begin(); iter != m_map.end(); ++ iter) { Entry* entry = iter->second; xmlpp::Element* child = elem.add_child(entry->get_name() ); entry->save(*child); } } Gobby::Config::Entry* Gobby::Config::ParentEntry:: get_child(const Glib::ustring& name) { return ptrmap_find(m_map, name); } const Gobby::Config::Entry* Gobby::Config::ParentEntry:: get_child(const Glib::ustring& name) const { return ptrmap_find(m_map, name); } Gobby::Config::ParentEntry* Gobby::Config::ParentEntry:: get_parent_child(const Glib::ustring& name) { return dynamic_cast(get_child(name) ); } const Gobby::Config::ParentEntry* Gobby::Config::ParentEntry:: get_parent_child(const Glib::ustring& name) const { return dynamic_cast(get_child(name) ); } Gobby::Config::ValueEntry* Gobby::Config::ParentEntry:: get_value_child(const Glib::ustring& name) { return dynamic_cast(get_child(name) ); } const Gobby::Config::ValueEntry* Gobby::Config::ParentEntry:: get_value_child(const Glib::ustring& name) const { return dynamic_cast(get_child(name) ); } bool Gobby::Config::ParentEntry::has_value(const Glib::ustring& name) const { return get_value_child(name) != NULL; } Gobby::Config::ParentEntry& Gobby::Config::ParentEntry:: operator[](const Glib::ustring& name) { ParentEntry* entry = get_parent_child(name); if(entry != NULL) return *entry; return set_parent(name); } Gobby::Config::ParentEntry& Gobby::Config::ParentEntry:: set_parent(const Glib::ustring& name) { Entry* entry = get_child(name); if(entry != NULL) delete entry; ParentEntry* child = new ParentEntry(name); m_map[name] = child; return *child; } Gobby::Config::ParentEntry::iterator Gobby::Config::ParentEntry::begin() { return iterator(m_map.begin() ); } Gobby::Config::ParentEntry::const_iterator Gobby::Config::ParentEntry:: begin() const { return const_iterator(m_map.begin() ); } Gobby::Config::ParentEntry::iterator Gobby::Config::ParentEntry::end() { return iterator(m_map.end() ); } Gobby::Config::ParentEntry::const_iterator Gobby::Config::ParentEntry:: end() const { return const_iterator(m_map.end() ); } Gobby::Config::Config(const Glib::ustring& file): m_filename(file) { xmlpp::DomParser parser; if(!Glib::file_test(file, Glib::FILE_TEST_IS_REGULAR)) { m_root.reset(new ParentEntry("gobby-config") ); return; } try { parser.parse_file(file); } catch(xmlpp::exception& e) { // Empty config m_root.reset(new ParentEntry("gobby-config") ); return; } xmlpp::Document* document = parser.get_document(); if(document == NULL) { m_root.reset(new ParentEntry("gobby-config") ); return; } xmlpp::Element* root = document->get_root_node(); // Config is present, but contains no root node if(root == NULL) { m_root.reset(new ParentEntry("gobby-config") ); return; } m_root.reset(new ParentEntry(*root) ); } Gobby::Config::~Config() { xmlpp::Document document; xmlpp::Element* root = document.create_root_node("gobby-config"); m_root->save(*root); try { Glib::ustring dirname = Glib::path_get_dirname(m_filename); create_directory_with_parents(dirname, 0700); document.write_to_file_formatted(m_filename, "UTF-8"); } catch(Glib::Exception& e) { g_warning("Could not write config file: %s", e.what().c_str() ); } catch(std::exception& e) { g_warning("Could not write config file: %s", e.what() ); } } Gobby::Config::ParentEntry& Gobby::Config::get_root() { return *m_root; } const Gobby::Config::ParentEntry& Gobby::Config::get_root() const { return *m_root; } std::string Gobby::serialize::default_context_to:: to_string(const data_type& from) const { return from; } Gobby::serialize::default_context_from::data_type Gobby::serialize::default_context_from:: from_string(const std::string& from) const { return from; } code/util/config.hpp000066400000000000000000000273011304552132100147220ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_CONFIG_HPP_ #define _GOBBY_CONFIG_HPP_ #include "serialize.hpp" #include #include #include #include #include // TODO: Use registry on windows, gconf with gnome #include #include namespace Gobby { namespace serialize { template<> class default_context_to: public context_base_to { public: typedef Glib::ustring data_type; virtual std::string to_string(const data_type& from) const; }; template<> class default_context_from: public context_base_from { public: typedef Glib::ustring data_type; virtual data_type from_string(const std::string& from) const; }; } // namespace serialize class Config { public: /** @brief Abstract base class for configuration file entries. */ class Entry { public: Entry(const Glib::ustring& name); virtual ~Entry() {} /** @brief Saves this entry into the given element. */ virtual void save(xmlpp::Element& elem) const = 0; /** @brief Returns the name of this entry. */ const Glib::ustring& get_name() const; protected: Glib::ustring m_name; }; /** @brief Entry that contains a value. */ class ValueEntry: public Entry { public: /** @brief Stores an object of the given type into this * entry. */ template ValueEntry( const Glib::ustring& name, const DataType& value, const serialize::context_base_to& ctx = serialize::default_context_to() ); /** @brief Returns the value of this entry and tries to * convert it to the requested type. */ template DataType get( const serialize::context_base_from& ctx = serialize::default_context_from() ) const; protected: serialize::data m_data; }; /** Value entry with type information. Useful for future storage * backends like gconf or windows registry. */ template class TypedValueEntry: public ValueEntry { public: /** @brief Creates a new typed value entry that is converted * to a string using the given context. */ TypedValueEntry( const Glib::ustring& name, const DataType& value, const serialize::context_base_to& ctx = serialize::default_context_to() ); /** @brief Reads a value entry from a xml element. */ TypedValueEntry(const xmlpp::Element& elem); /** @brief Stores this entry into the given element. */ virtual void save(xmlpp::Element& elem) const; }; /** @brief Entry containing child entries. */ class ParentEntry: public Entry { protected: typedef std::map map_type; public: template class iterator_base { public: typedef BaseIterator base_iterator; iterator_base(const base_iterator& iter); iterator_base& operator++(); iterator_base operator++(int); bool operator==(const iterator_base& other) const; bool operator!=(const iterator_base& other) const; Entry& operator*() const; Entry* operator->() const; protected: base_iterator m_iter; }; typedef iterator_base iterator; typedef iterator_base const_iterator; /** @brief Creates a new ParentEntry of the given name with * no children. */ ParentEntry(const Glib::ustring& name); /** @brief a new ParentEntry from the given xml element. */ ParentEntry(const xmlpp::Element& elem); virtual ~ParentEntry(); /** @brief Stores this ParentEntry into the given xml element. */ virtual void save(xmlpp::Element& elem) const; /** @brief Returns a child entry with the given name. * * Returns NULL if there is no such child. */ Entry* get_child(const Glib::ustring& name); /** @brief Returns a child entry with the given name. * * Returns NULL if there is no such child. */ const Entry* get_child(const Glib::ustring& name) const; /** @brief Returns a child that is another parent entry * and has the given name. * * Returns NULL if there is no such child. */ ParentEntry* get_parent_child(const Glib::ustring& name); /** @brief Returns a child that is another parent entry * and has the given name. * * Returns NULL if there is no such child. */ const ParentEntry* get_parent_child(const Glib::ustring& name) const; /** @brief Returns a child that is a value entry and has * the given name. * * Returns NULL if there is no such child. */ ValueEntry* get_value_child(const Glib::ustring& name); /** @brief Returns a child that is a value entry and has * the given name. * * Returns NULL if there is no such child. */ const ValueEntry* get_value_child(const Glib::ustring& name) const; /** @brief: Returns whether there is a child ValueEntry with * the given name. */ bool has_value(const Glib::ustring& name) const; /** @brief Returns the value from the child with the given * name. * * If there is no such child (or it is not a ValueEntry), the * given default value is returned. */ template DataType get_value( const Glib::ustring& name, const DataType& default_value = DataType(), const serialize::context_base_from& ctx = serialize::default_context_from() ) const; /** @brief Returns the value from the child with the given * name. * * If there is no such child (or it is not a ValueEntry), a * new child will be created (replacing a potential old one) * and assigned the given default value. */ template DataType supply_value( const Glib::ustring& name, const DataType& default_value = DataType(), const serialize::context_base_from& ctx_from = serialize::default_context_from(), const serialize::context_base_to& ctx_to = serialize::default_context_to() ); /** @brief Creates a new child ValueEntry with the given name * and value. * * If there is already a child with this name, it will be * removed. */ template void set_value( const Glib::ustring& name, const DataType& value, const serialize::context_base_to& ctx = serialize::default_context_to() ); /** @brief Returns the parent entry at name. * * If there is no parent node, a new one will be created * that overwrites the current entry (if any). */ ParentEntry& operator[](const Glib::ustring& name); /** @brief Creates a new ParentEntry with the given name. * * If there is already a child with this name, it will be * removed. */ ParentEntry& set_parent(const Glib::ustring& name); /** @brief Returns an iterator to the beginning of the * child entry sequence. */ iterator begin(); /** @brief Returns an iterator to the beginning of the * child entry sequence. */ const_iterator begin() const; /** @brief Returns an iterator to the end of the child * entry sequence. */ iterator end(); /** @brief Returns an iterator to the end of the child * entry sequence. */ const_iterator end() const; protected: map_type m_map; }; Config(const Glib::ustring& file); ~Config(); ParentEntry& get_root(); const ParentEntry& get_root() const; protected: Glib::ustring m_filename; std::unique_ptr m_root; }; template Config::ValueEntry:: ValueEntry(const Glib::ustring& name, const DataType& value, const serialize::context_base_to& ctx): Entry(name), m_data(value, ctx) { } template DataType Config::ValueEntry:: get(const serialize::context_base_from& from) const { return m_data.Gobby::serialize::data::as(from); } template Config::TypedValueEntry:: TypedValueEntry(const Glib::ustring& name, const DataType& value, const serialize::context_base_to& ctx): ValueEntry(name, value, ctx) { } template Config::TypedValueEntry::TypedValueEntry(const xmlpp::Element& elem): ValueEntry(elem.get_name(), elem.get_child_text()->get_content() ) { } template void Config::TypedValueEntry::save(xmlpp::Element& elem) const { elem.set_child_text(m_data.serialized() ); } template Config::ParentEntry::iterator_base:: iterator_base(const base_iterator& iter): m_iter(iter) { } template Config::ParentEntry::iterator_base& Config::ParentEntry::iterator_base::operator++() { ++ m_iter; return *this; } template Config::ParentEntry::iterator_base Config::ParentEntry::iterator_base::operator++(int) { iterator_base temp(*this); ++ m_iter; return temp; } template bool Config::ParentEntry::iterator_base:: operator==(const iterator_base& other) const { return m_iter == other.m_iter; } template bool Config::ParentEntry::iterator_base:: operator!=(const iterator_base& other) const { return m_iter != other.m_iter; } template Entry& Config::ParentEntry::iterator_base:: operator*() const { return *m_iter->second; } template Entry* Config::ParentEntry::iterator_base:: operator->() const { return m_iter->second; } template DataType Config::ParentEntry:: get_value(const Glib::ustring& name, const DataType& default_value, const serialize::context_base_from& ctx) const { const ValueEntry* entry = get_value_child(name); if(entry == NULL) return default_value; return entry->ValueEntry::get(ctx); } template DataType Config::ParentEntry:: supply_value(const Glib::ustring& name, const DataType& default_value, const serialize::context_base_from& ctx_from, const serialize::context_base_to& ctx_to) { ValueEntry* entry = get_value_child(name); if(entry != NULL) return entry->get(ctx_from); set_value(name, default_value, ctx_to); return default_value; } template void Config::ParentEntry:: set_value(const Glib::ustring& name, const DataType& value, const serialize::context_base_to& ctx) { Entry* entry = get_child(name); if(entry != NULL) delete entry; m_map[name] = new TypedValueEntry(name, value, ctx); } } // namespace Gobby #endif // _GOBBY_CONFIG_HPP_ code/util/defaultaccumulator.hpp000066400000000000000000000026621304552132100173440ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_DEFAULT_ACCUMULATOR_HPP_ #define _GOBBY_DEFAULT_ACCUMULATOR_HPP_ namespace Gobby { /** Accumulator for signals with return type that defaults to a value if no * signal handler is connected. */ template class default_accumulator { public: typedef return_type result_type; template result_type operator()(iterator begin, iterator end) const { return_type result = default_return; for(; begin != end; ++ begin) result = *begin; return result; } }; } // namespace Gobby #endif // _GOBBY_DEFAULT_ACCUMULATOR_HPP_ code/util/file.cpp000066400000000000000000000052731304552132100143730ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "util/file.hpp" #include "util/i18n.hpp" #include #include #include #include // For mkdir / CreateDirectory #ifdef WIN32 #include #else #include #include #include #endif namespace { void create_directory(const char* path, int mode) { #ifdef WIN32 // TODO: Use widechar API? if(CreateDirectoryA(path, NULL) == FALSE) { LPVOID msgbuf; DWORD err = GetLastError(); // TODO: Use widechar API? FormatMessageA( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), reinterpret_cast(&msgbuf), 0, NULL ); std::string error_message = static_cast(msgbuf); LocalFree(msgbuf); // TODO: Convert to UTF-8? throw std::runtime_error( Glib::ustring::compose( Gobby::_("Could not create directory " "\"%1\": %2"), std::string(path), error_message)); } #else if(g_mkdir(path, mode) == -1) { throw std::runtime_error( Glib::ustring::compose( Gobby::_("Could not create directory " "\"%1\": %2"), std::string(path), std::strerror(errno))); } #endif } } namespace Gobby { void create_directory_with_parents(const std::string& path, int mode) { // Directory exists, nothing to do if(Glib::file_test(path, Glib::FILE_TEST_IS_DIR) ) return; // Find path to the directory to create std::string path_to = Glib::path_get_dirname(path); // Create this path, if it doesn't exists create_directory_with_parents(path_to, mode); // Create new directory create_directory(path.c_str(), mode); } std::string config_filename(const std::string& filename) { return Glib::build_filename( Glib::build_filename( Glib::get_user_config_dir(), "gobby"), filename); } } code/util/file.hpp000066400000000000000000000021111304552132100143640ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_DIR_HPP_ #define _GOBBY_DIR_HPP_ #include namespace Gobby { void create_directory_with_parents(const std::string& path, int mode=0755); std::string config_filename(const std::string& filename); } #endif // _GOBBY_DIR_HPP_ code/util/historyentry.cpp000066400000000000000000000224021304552132100162300ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "util/historyentry.hpp" #include #include namespace { Glib::ustring strip(const Glib::ustring& string) { gchar* dup = g_strdup(string.c_str()); dup = g_strchug(dup); Glib::ustring retval(dup); g_free(dup); return retval; } bool strequal(const Glib::ustring& string1, const Glib::ustring& string2) { gchar* casefold1 = g_utf8_casefold(string1.c_str(), -1); gchar* casefold2 = g_utf8_casefold(string2.c_str(), -1); int collate = g_utf8_collate(casefold1, casefold2); g_free(casefold1); g_free(casefold2); return collate == 0; } } // Load history asynchronously, to save startup time class Gobby::History::Loader { public: Loader(History& history); private: void close(); void add(const std::string& str); void process(unsigned int size); void on_read(const Glib::RefPtr& result); void on_stream_read(const Glib::RefPtr& result); History& m_history; Glib::RefPtr m_file; Glib::RefPtr m_stream; std::string m_line; static const unsigned int BUFFER_SIZE = 1024; char m_buffer[BUFFER_SIZE]; }; Gobby::History::Loader::Loader(History& history): m_history(history) { m_file = Gio::File::create_for_path(m_history.m_history_file); m_file->read_async(sigc::mem_fun(*this, &Loader::on_read)); } void Gobby::History::Loader::close() { m_history.m_loader.reset(NULL); } void Gobby::History::Loader::add(const std::string& str) { if(!str.empty()) { Gtk::TreeIter iter = m_history.m_history->append(); (*iter)[m_history.m_history_columns.text] = str; } } void Gobby::History::Loader::process(unsigned int size) { const char* pos = m_buffer; const char* end = m_buffer + size; const char* next; const Gtk::TreeNodeChildren& children = m_history.m_history->children(); while((next = std::find(pos, end, '\n')) != end) { if(!m_line.empty()) { add(m_line + std::string(pos, next - pos)); m_line.clear(); } else { add(std::string(pos, next - pos)); } if(children.size() == m_history.m_length) break; pos = next + 1; } if(children.size() == m_history.m_length) { close(); } else { m_line.append(pos, end - pos); m_stream->read_async( m_buffer, BUFFER_SIZE, sigc::mem_fun(*this, &Loader::on_stream_read)); } } void Gobby::History::Loader::on_read( const Glib::RefPtr& result) { try { m_stream = m_file->read_finish(result); m_stream->read_async( m_buffer, BUFFER_SIZE, sigc::mem_fun(*this, &Loader::on_stream_read)); } catch(const Glib::Exception& ex) { close(); } } void Gobby::History::Loader::on_stream_read( const Glib::RefPtr& result) { gssize size; try { size = m_stream->read_finish(result); // This should have caused an exception g_assert(size >= 0); if(size == 0) { // EOF if(!m_line.empty()) add(m_line); close(); } else { process(size); } } catch(const Glib::Exception& ex) { close(); } } Gobby::History::History(const std::string& history_file, unsigned int length): m_length(length), m_history(Gtk::ListStore::create(m_history_columns)), m_current(m_history->children().end()), m_history_file(history_file), m_loader(new Loader(*this)) { } Gobby::History::History(unsigned int length): m_length(length), m_history(Gtk::ListStore::create(m_history_columns)), m_current(m_history->children().end()) { } Gobby::History::~History() { try { if(!m_history_file.empty()) { Glib::RefPtr file = Gio::File::create_for_path(m_history_file); Glib::RefPtr stream = file->replace(); const Gtk::TreeNodeChildren& children = m_history->children(); for(Gtk::TreeIter iter = children.begin(); iter != children.end(); ++ iter) { const Glib::ustring& str = (*iter)[m_history_columns.text]; gsize bytes_written; stream->write_all(str, bytes_written); g_assert(bytes_written == str.length()); stream->write_all("\n", bytes_written); g_assert(bytes_written == 1); } } } catch(const Glib::Exception& error) { // Ignore } } Glib::RefPtr Gobby::History::get_store() { return m_history; } const Gobby::History::Columns& Gobby::History::get_columns() const { return m_history_columns; } bool Gobby::History::up(const Glib::ustring& current, Glib::ustring& entry) { if(m_current == m_history->children().end()) { m_current = m_history->children().begin(); // No entries in list: if(m_current == m_history->children().end()) return false; if(!current.empty()) commit_noscroll(current); } else { ++ m_current; if(m_current == m_history->children().end()) { -- m_current; return false; } } entry = (*m_current)[m_history_columns.text]; return true; } bool Gobby::History::down(const Glib::ustring& current, Glib::ustring& entry) { if(m_current == m_history->children().end()) { if(!current.empty()) { entry.clear(); commit_noscroll(current); return true; } else { return false; } } if(m_current == m_history->children().begin()) { m_current = m_history->children().end(); entry.clear(); return true; } -- m_current; entry = (*m_current)[m_history_columns.text]; return true; } void Gobby::History::commit(const Glib::ustring& str) { commit_noscroll(str); m_current = m_history->children().end(); } void Gobby::History::commit_noscroll(const Glib::ustring& str) { const Glib::ustring stripped = strip(str); // Check if the entry already exists in the list, and if yes, // remove it. for(Gtk::TreeIter iter = m_history->children().begin(); iter != m_history->children().end(); ++iter) { const Glib::ustring& entry = (*iter)[m_history_columns.text]; if(strequal(entry, stripped)) { m_history->erase(iter); break; } } Gtk::TreeIter iter = m_history->prepend(); (*iter)[m_history_columns.text] = stripped; while(m_history->children().size() > m_length) { iter = m_history->children().end(); -- iter; m_history->erase(iter); } } Gobby::HistoryEntry::HistoryEntry(const std::string& history_file, unsigned int length): m_history(history_file, length) { } Gobby::HistoryEntry::HistoryEntry(unsigned int length): m_history(length) { } void Gobby::HistoryEntry::commit() { m_history.commit(get_text()); } bool Gobby::HistoryEntry::on_key_press_event(GdkEventKey* event) { Glib::ustring entry; if(event->keyval == GDK_KEY_Up) { if(m_history.up(get_text(), entry)) set_text(entry); return true; } if(event->keyval == GDK_KEY_Down) { if(m_history.down(get_text(), entry)) set_text(entry); return true; } return Gtk::Entry::on_key_press_event(event); } Gobby::HistoryComboBox::HistoryComboBox( const Glib::RefPtr& builder, const char* id, const std::string& history_file, unsigned int length) : Gtk::ComboBox(GTK_COMBO_BOX( gtk_builder_get_object(builder->gobj(), id))), m_history(history_file, length) { set_model(m_history.get_store()); set_entry_text_column(m_history.get_columns().text); get_entry()->signal_key_press_event().connect( sigc::mem_fun( *this, &HistoryComboBox::on_entry_key_press_event), false); } Gobby::HistoryComboBox::HistoryComboBox(const std::string& history_file, unsigned int length): Gtk::ComboBox(true), m_history(history_file, length) { set_model(m_history.get_store()); set_entry_text_column(m_history.get_columns().text); get_entry()->signal_key_press_event().connect( sigc::mem_fun( *this, &HistoryComboBox::on_entry_key_press_event), false); } Gobby::HistoryComboBox::HistoryComboBox(unsigned int length): Gtk::ComboBox(true), m_history(length) { set_model(m_history.get_store()); set_entry_text_column(m_history.get_columns().text); get_entry()->signal_key_press_event().connect( sigc::mem_fun( *this, &HistoryComboBox::on_entry_key_press_event), false); } void Gobby::HistoryComboBox::commit() { m_history.commit(get_entry()->get_text()); } Glib::RefPtr Gobby::HistoryComboBox::get_accessible() { return get_entry()->get_accessible(); } bool Gobby::HistoryComboBox::on_entry_key_press_event(GdkEventKey* event) { Glib::ustring entry; if(event->keyval == GDK_KEY_Up) { if(m_history.up(get_entry()->get_text(), entry)) get_entry()->set_text(entry); return true; } if(event->keyval == GDK_KEY_Down) { if(m_history.down(get_entry()->get_text(), entry)) get_entry()->set_text(entry); return true; } return false; } code/util/historyentry.hpp000066400000000000000000000052101304552132100162330ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_HISTORYENTRY_HPP_ #define _GOBBY_HISTORYENTRY_HPP_ #include #include #include #include #include #include namespace Gobby { class History { public: class Columns: public Gtk::TreeModelColumnRecord { public: Columns() { add(text); } Gtk::TreeModelColumn text; }; History(const std::string& history_file, unsigned int length); History(unsigned int length); ~History(); Glib::RefPtr get_store(); const Columns& get_columns() const; bool up(const Glib::ustring& current, Glib::ustring& entry); bool down(const Glib::ustring& current, Glib::ustring& entry); void commit(const Glib::ustring& str); protected: void commit_noscroll(const Glib::ustring& str); const unsigned int m_length; const Columns m_history_columns; Glib::RefPtr m_history; Gtk::TreeIter m_current; std::string m_history_file; private: class Loader; std::unique_ptr m_loader; }; class HistoryEntry: public Gtk::Entry { public: HistoryEntry(const std::string& history_file, unsigned int length); HistoryEntry(unsigned int length); void commit(); protected: virtual bool on_key_press_event(GdkEventKey* event); History m_history; }; class HistoryComboBox: public Gtk::ComboBox { public: HistoryComboBox(const Glib::RefPtr& builder, const char* id, const std::string& history_file, unsigned int length); HistoryComboBox(const std::string& history_file, unsigned int length); HistoryComboBox(unsigned int length); void commit(); Glib::RefPtr get_accessible(); protected: bool on_entry_key_press_event(GdkEventKey* event); History m_history; }; } // namespace Gobby #endif // _GOBBY_HISTORYENTRY_HPP_ code/util/i18n.cpp000066400000000000000000000023031304552132100142220ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "features.hpp" #include "util/i18n.hpp" #include const char* Gobby::_(const char* msgid) { return dgettext(GETTEXT_PACKAGE, msgid); } const char* Gobby::ngettext(const char* msgid, const char* msgid_plural, unsigned long int n) { return dngettext(GETTEXT_PACKAGE, msgid, msgid_plural, n); } code/util/i18n.hpp000066400000000000000000000023621304552132100142340ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_I18N_HPP_ #define _GOBBY_I18N_HPP_ #ifndef N_ # define N_(id) (id) #endif namespace Gobby { /** Translates a message from the gobby catalog. */ const char* _(const char* msgid); /** Translate pluralized message from the gobby catalog. */ const char* ngettext(const char* msgid, const char* msgid_plural, unsigned long int n); } #endif // _GOBBY_I18N_HPP_ code/util/serialize.cpp000066400000000000000000000046071304552132100154430ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "util/serialize.hpp" const char* Gobby::serialize::type_name::name = "int"; const char* Gobby::serialize::type_name::name = "long"; const char* Gobby::serialize::type_name::name = "short"; const char* Gobby::serialize::type_name::name = "char"; const char* Gobby::serialize::type_name::name = "unsigned int"; const char* Gobby::serialize::type_name::name = "unsigned long"; const char* Gobby::serialize::type_name::name = "unsigned short"; const char* Gobby::serialize::type_name::name = "unsigned char"; const char* Gobby::serialize::type_name::name = "float"; const char* Gobby::serialize::type_name::name = "double"; const char* Gobby::serialize::type_name::name = "long double"; const char* Gobby::serialize::type_name::name = "bool"; Gobby::serialize::conversion_error:: conversion_error(const std::string& message): std::runtime_error(message) { } Gobby::serialize::data::data(const std::string& serialized): m_serialized(serialized) { } const std::string& Gobby::serialize::data::serialized() const { return m_serialized; } std::string Gobby::serialize::default_context_to:: to_string(const data_type& from) const { return from; } Gobby::serialize::default_context_from::data_type Gobby::serialize::default_context_from:: from_string(const data_type& from) const { return from; } std::string Gobby::serialize::default_context_to:: to_string(const data_type& from) const { return from; } code/util/serialize.hpp000066400000000000000000000163561304552132100154540ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_SERIALIZE_HPP_ #define _GOBBY_SERIALIZE_HPP_ #include #include #include namespace Gobby { /** Generic stuff to de/serialize data types to/from strings. */ namespace serialize { /** Error that is thrown if conversion from a string fails. For example, if * "t3" should be converted to int. */ class conversion_error: public std::runtime_error { public: conversion_error(const std::string& message); }; /** @brief Several built-in type names. */ template struct type_name {}; template<> struct type_name { static const char* name; }; template<> struct type_name { static const char* name; }; template<> struct type_name { static const char* name; }; template<> struct type_name { static const char* name; }; template<> struct type_name { static const char* name; }; template<> struct type_name { static const char* name; }; template<> struct type_name { static const char* name; }; template<> struct type_name { static const char* name; }; template<> struct type_name { static const char* name; }; template<> struct type_name { static const char* name; }; template<> struct type_name { static const char* name; }; template<> struct type_name { static const char* name; }; /** Abstract base context type to convert something to a string. */ template class context_base_to { public: virtual ~context_base_to() {} /** @brief Converts the given data type to a string. */ virtual std::string to_string(const data_type& from) const = 0; }; /** Abstract base context type to convert a string to another type. */ template class context_base_from { public: virtual ~context_base_from() {} /** @brief Converts a string to a data type. Might throw * serialize::conversion_error. */ virtual data_type from_string(const std::string& from) const = 0; }; /** Default context to convert something literally to a string. */ template class default_context_to: public context_base_to { public: /** @brief Converts the given data type to a string. */ virtual std::string to_string(const data_type& from) const; protected: /** Method derived classes may overload to alter the conversion. */ virtual void on_stream_setup(std::stringstream& stream) const; }; /** Default context to convert a string literally to a type. */ template class default_context_from: public context_base_from { public: /** @brief Converts the given string to the type specified * as template parameter. * * May throw serialize::conversion_error(). */ virtual data_type from_string(const std::string& from) const; protected: /** Method derived classes may overload to alter the conversion. */ virtual void on_stream_setup(std::stringstream& stream) const; }; #if 0 /** Context that uses hexadecimal representation for numerical types. */ template class hex_context_to: public default_context_to { protected: virtual void on_stream_setup(std::stringstream& stream) const; }; /** Context that uses hexadecimal representation for numerical types. */ template class hex_context_from: public default_context_from { public: virtual void on_stream_setup(std::stringstream& stream) const; }; #endif template<> class default_context_to: public context_base_to { public: typedef std::string data_type; virtual std::string to_string(const data_type& from) const; }; template<> class default_context_from: public context_base_from { public: typedef std::string data_type; virtual data_type from_string(const std::string& from) const; }; template<> class default_context_to: public context_base_to { public: typedef const char* data_type; virtual std::string to_string(const data_type& from) const; }; template class default_context_to: public context_base_to { public: typedef char data_type[N]; virtual std::string to_string(const data_type& from) const; }; /** A serialized object. */ class data { public: /** Uses the given string as serialized data without converting it. */ data(const std::string& serialized); /** Serialises the given object with the given context. A default * context is used if no one is given. */ template data(const type& data, const context_base_to& ctx = default_context_to()); /** Returns the serialized data. */ const std::string& serialized() const; /** Deserializes the object with the given context. A default context * is used of no one is given. */ template type as(const context_base_from& ctx = default_context_from()) const; protected: std::string m_serialized; }; template std::string default_context_to:: to_string(const data_type& from) const { std::stringstream stream; on_stream_setup(stream); stream << from; return stream.str(); } template data_type default_context_from:: from_string(const std::string& from) const { std::stringstream stream(from); on_stream_setup(stream); data_type data; stream >> data; if(stream.bad() ) { throw conversion_error( "Could not convert \"" + from + "\" to " + type_name::name ); } return data; } template void default_context_to:: on_stream_setup(std::stringstream& stream) const { } template void default_context_from:: on_stream_setup(std::stringstream& stream) const { } #if 0 template void hex_context_to:: on_stream_setup(std::stringstream& stream) const { stream << std::hex; } template void hex_context_from:: on_stream_setup(std::stringstream& stream) const { stream >> std::hex; } #endif template data::data(const data_type& data, const context_base_to& ctx): m_serialized(ctx.to_string(data) ) { } template data_type data::as(const context_base_from& ctx) const { return ctx.from_string(m_serialized); } template std::string default_context_to:: to_string(const data_type& from) const { return from; } } // namespace serialize } // namespace Gobby #endif // _GOBBY_SERIALIZE_HPP_ code/util/uri.cpp000066400000000000000000000056661304552132100142610ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "util/uri.hpp" #include "util/i18n.hpp" #include #include #ifndef G_OS_WIN32 # include # include #endif namespace Gobby { void parse_uri(const std::string& uri, std::string& scheme, std::string& netloc, std::string& path) { // First, parse the scheme. If there is no scheme found, default // to infinote: std::string::size_type scheme_delim = uri.find("://"); if(scheme_delim == std::string::npos) { scheme = "infinote"; scheme_delim = 0; } else { scheme = uri.substr(0, scheme_delim); // Skip ':' and all following '/' do { ++scheme_delim; } while(uri[scheme_delim] == '/'); } std::string::size_type path_delim = uri.find('/', scheme_delim); if(path_delim == std::string::npos) { netloc = uri.substr(scheme_delim); path.clear(); } else { netloc = Glib::uri_unescape_string( uri.substr(scheme_delim, path_delim - scheme_delim)); path = Glib::uri_unescape_string(uri.substr(path_delim)); } } void parse_netloc(const std::string& netloc, std::string& hostname, std::string& service, unsigned int& device_index) { std::string str = netloc; service = "6523"; // Default device_index = 0; // Default // Strip away device name std::string::size_type pos; if( (pos = str.rfind('%')) != std::string::npos) { std::string device_name = str.substr(pos + 1); str.erase(pos); #ifdef G_OS_WIN32 // TODO: Implement device_index = 0; #else device_index = if_nametoindex(device_name.c_str()); if(device_index == 0) { throw std::runtime_error( _("Device \"%1\" does not exist")); } #endif } if(str[0] == '[' && ((pos = str.find(']', 1)) != std::string::npos)) { // Hostname surrounded by '[...]' hostname = str.substr(1, pos-1); ++ pos; if(pos < str.length() && str[pos] == ':') service = str.substr(pos + 1); } else { pos = str.find(':'); if(pos != std::string::npos) { hostname = str.substr(0, pos); service = str.substr(pos + 1); } else { hostname = str; } } } } // namespace Gobby code/util/uri.hpp000066400000000000000000000024171304552132100142550ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_URI_HPP_ #define _GOBBY_URI_HPP_ #include namespace Gobby { void parse_uri(const std::string& uri, std::string& scheme, std::string& netloc, std::string& path); void parse_netloc(const std::string& netloc, std::string& hostname, std::string& service, unsigned int& device_index); } #endif // _GOBBY_URI_HPP_ code/window.cpp000066400000000000000000000223331304552132100140020ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2015 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "features.hpp" #include "window.hpp" #include "commands/file-tasks/task-open.hpp" #include "commands/file-tasks/task-open-multiple.hpp" #include "util/i18n.hpp" #include Gobby::Window::Window(Config& config, GtkSourceLanguageManager* language_manager, FileChooser& file_chooser, Preferences& preferences, CertificateManager& cert_manager): m_config(config), m_lang_manager(language_manager), m_file_chooser(file_chooser), m_preferences(preferences), m_cert_manager(cert_manager), m_connection_manager(cert_manager, preferences), m_text_folder(false, m_preferences, m_lang_manager), m_chat_folder(true, m_preferences, m_lang_manager), m_statusbar(m_text_folder, m_preferences), m_toolbar(m_preferences), m_browser(*this, m_statusbar, m_connection_manager), m_chat_frame(_("Chat"), "chat", m_preferences.appearance.show_chat), m_actions(*this, m_preferences), m_info_storage(INF_GTK_BROWSER_MODEL(m_browser.get_store())), m_host_storage(m_browser), m_folder_manager(m_browser, m_info_storage, m_text_folder, m_chat_folder), m_operations(m_info_storage, m_browser, m_folder_manager, m_statusbar), m_auth_commands(*this, m_browser, m_statusbar, m_connection_manager, m_preferences), m_self_hoster(m_connection_manager.get_io(), m_connection_manager.get_communication_manager(), m_connection_manager.get_publisher(), m_auth_commands.get_sasl_context(), m_statusbar, m_cert_manager, m_preferences), m_browser_commands(m_browser, m_folder_manager, m_statusbar, m_operations, m_preferences), m_browser_context_commands(*this, m_connection_manager.get_io(), m_browser, m_file_chooser, m_operations, m_cert_manager, m_preferences), m_autosave_commands(m_text_folder, m_operations, m_info_storage, m_preferences), m_subscription_commands(m_text_folder, m_chat_folder), m_synchronization_commands(m_text_folder, m_chat_folder), m_user_join_commands(m_folder_manager, m_preferences), m_text_folder_commands(m_text_folder), m_chat_folder_commands(m_chat_folder), m_file_commands(*this, m_actions, m_browser, m_folder_manager, m_statusbar, m_file_chooser, m_operations, m_info_storage, m_preferences), m_edit_commands(*this, m_actions, m_text_folder, m_statusbar), m_view_commands(*this, m_actions, m_lang_manager, m_text_folder, m_chat_frame, m_chat_folder, m_preferences), m_title_bar(*this, m_text_folder) { m_chat_frame.signal_show().connect( sigc::mem_fun(*this, &Window::on_chat_show), true); m_chat_frame.signal_hide().connect( sigc::mem_fun(*this, &Window::on_chat_hide), false); m_browser.add_browser(INF_BROWSER(m_self_hoster.get_directory()), _("This Computer")); m_toolbar.show(); m_browser.show(); m_text_folder.show(); m_chat_folder.show(); // Build UI Glib::RefPtr group = Gtk::AccelGroup::create(); // Add focus shortcuts; unfortunately gtkmm does not wrap that API GClosure* closure = g_cclosure_new( G_CALLBACK(on_switch_to_chat_static), this, NULL); gtk_accel_group_connect(group->gobj(), GDK_KEY_m, GDK_CONTROL_MASK, static_cast(0), closure); //g_closure_unref(closure); GClosure* closure2 = g_cclosure_new( G_CALLBACK(on_switch_to_text_static), this, NULL); gtk_accel_group_connect(group->gobj(), GDK_KEY_m, static_cast( GDK_CONTROL_MASK | GDK_SHIFT_MASK), static_cast(0), closure2); //g_closure_unref(closure2); add_accel_group(group); Gtk::Frame* frame_browser = Gtk::manage(new ClosableFrame( _("Document Browser"), "document-list", m_preferences.appearance.show_browser)); frame_browser->set_shadow_type(Gtk::SHADOW_IN); frame_browser->add(m_browser); // frame_browser manages visibility itself Gtk::Frame* frame_text = Gtk::manage(new Gtk::Frame); frame_text->set_shadow_type(Gtk::SHADOW_IN); frame_text->add(m_text_folder); frame_text->show(); m_chat_frame.set_shadow_type(Gtk::SHADOW_IN); m_chat_frame.add(m_chat_folder); // frame_chat manages visibility itself m_chat_paned.pack1(*frame_text, true, false); m_chat_paned.pack2(m_chat_frame, false, false); m_chat_paned.show(); m_paned.pack1(*frame_browser, false, false); m_paned.pack2(m_chat_paned, true, false); m_paned.show(); m_grid.set_orientation(Gtk::ORIENTATION_VERTICAL); m_grid.attach(m_toolbar, 0, 0, 1, 1); m_grid.attach(m_paned, 0, 1, 1, 1); m_grid.attach(m_statusbar, 0, 2, 1, 1); m_grid.show(); // Give initial focus to the browser, which will in turn give focus // to the "Direct Connection" expander, so people can quickly // get going. set_focus_child(m_browser); add(m_grid); set_default_size(800, 600); set_role("Gobby"); } void Gobby::Window::subscribe(const Glib::ustring& uri) { m_operations.subscribe_path(uri); } void Gobby::Window::open_files(const Operations::file_list& files) { if(files.size() == 1) { m_file_commands.set_task( new TaskOpen(m_file_commands, files[0])); } else if(files.size() > 1) { m_file_commands.set_task( new TaskOpenMultiple(m_file_commands, files)); } } // GtkWindow catches keybindings for the menu items _before_ passing them to // the focused widget. This is unfortunate and means that pressing ctrl+V // in an entry on the browser ends up pasting text in the TextView. // Here we override GtkWindow's handler to do the same things that it // does, but in the opposite order and then we chain up to the grand // parent handler, skipping Gtk::Window::key_press_event(). // This code is basically stolen from gedit, but ported to C++. bool Gobby::Window::on_key_press_event(GdkEventKey* event) { bool handled = gtk_window_propagate_key_event(GTK_WINDOW(gobj()), event); if(!handled) handled = gtk_window_activate_key(GTK_WINDOW(gobj()), event); // Skip Gtk::Window default handler here: if(!handled) handled = Gtk::Container::on_key_press_event(event); return handled; } void Gobby::Window::on_realize() { Gtk::Window::on_realize(); m_paned.set_position(m_paned.get_width() * 2 / 5); m_chat_paned.set_position(m_chat_paned.get_height() * 7 / 10); } void Gobby::Window::on_show() { Gtk::Window::on_show(); Glib::RefPtr settings( Gio::Settings::create("de.0x539.gobby.state.initial")); // Migrate from config.xml const Config::ParentEntry& entry = m_config.get_root()["initial"]; if(entry.has_value("run")) settings->set_boolean("run", entry.get_value("run")); if(!settings->get_boolean("run")) { m_initial_dlg = InitialDialog::create( *this, m_statusbar, m_preferences, m_cert_manager); m_initial_dlg->present(); m_initial_dlg->signal_hide().connect( sigc::mem_fun(*this, &Window::on_initial_dialog_hide)); } } void Gobby::Window::on_initial_dialog_hide() { m_initial_dlg.reset(NULL); // Don't show again Glib::RefPtr settings( Gio::Settings::create("de.0x539.gobby.state.initial")); settings->set_boolean("run", true); } bool Gobby::Window::on_switch_to_chat() { SessionView* view = m_chat_folder.get_current_document(); if(!view) return false; ChatSessionView* chat_view = dynamic_cast(view); if(!chat_view) return false; m_preferences.appearance.show_chat = true; InfGtkChat* chat = chat_view->get_chat(); GtkWidget* entry = inf_gtk_chat_get_entry(chat); gtk_widget_grab_focus(GTK_WIDGET(entry)); return true; } bool Gobby::Window::on_switch_to_text() { SessionView* view = m_text_folder.get_current_document(); if(!view) return false; TextSessionView* text_view = dynamic_cast(view); if(!text_view) return false; GtkSourceView* gtk_view = text_view->get_text_view(); gtk_widget_grab_focus(GTK_WIDGET(gtk_view)); // TODO: Turn chat back off if previously activated // via on_switch_to_chat()? return true; } void Gobby::Window::on_chat_hide() { Gtk::Widget* focus = get_focus(); // Actually this always returns NULL if m_chat_frame has focus, // because the focus is removed again. I think it's good enough // though. if(focus == NULL || focus == &m_chat_frame || focus->is_ancestor(m_chat_frame)) { on_switch_to_text(); } } void Gobby::Window::on_chat_show() { Gtk::Widget* focus = get_focus(); if(!focus) on_switch_to_chat(); } code/window.hpp000066400000000000000000000122631304552132100140100ustar00rootroot00000000000000/* Gobby - GTK-based collaborative text editor * Copyright (C) 2008-2014 Armin Burgmeier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _GOBBY_WINDOW_HPP_ #define _GOBBY_WINDOW_HPP_ #include "features.hpp" #include "commands/file-tasks/task-open.hpp" #include "commands/file-tasks/task-open-multiple.hpp" #include "commands/autosave-commands.hpp" #include "commands/browser-commands.hpp" #include "commands/subscription-commands.hpp" #include "commands/synchronization-commands.hpp" #include "commands/user-join-commands.hpp" #include "commands/browser-context-commands.hpp" #include "commands/auth-commands.hpp" #include "commands/folder-commands.hpp" #include "commands/file-commands.hpp" #include "commands/edit-commands.hpp" #include "commands/view-commands.hpp" #include "operations/operations.hpp" #include "dialogs/initial-dialog.hpp" #include "core/selfhoster.hpp" #include "core/toolbar.hpp" #include "core/folder.hpp" #include "core/browser.hpp" #include "core/statusbar.hpp" #include "core/preferences.hpp" #include "core/filechooser.hpp" #include "core/closableframe.hpp" #include "core/titlebar.hpp" #include "core/windowactions.hpp" #include "core/knownhoststorage.hpp" #include "util/config.hpp" #include #include #include #include namespace Gobby { class Window : public Gtk::ApplicationWindow { public: Window(Config& config, GtkSourceLanguageManager* language_manager, FileChooser& file_chooser, Preferences& preferences, CertificateManager& cert_manager); void subscribe(const Glib::ustring& uri); void open_files(const Operations::file_list& files); protected: // Gtk::Window overrides virtual bool on_key_press_event(GdkEventKey* event); virtual void on_realize(); virtual void on_show(); void on_initial_dialog_hide(); static gboolean on_switch_to_chat_static(GtkAccelGroup* group, GObject* acceleratable, guint keyval, GdkModifierType modifier, gpointer user_data) { return static_cast(user_data)->on_switch_to_chat(); } static gboolean on_switch_to_text_static(GtkAccelGroup* group, GObject* acceleratable, guint keyval, GdkModifierType modifier, gpointer user_data) { return static_cast(user_data)->on_switch_to_text(); } bool on_switch_to_chat(); bool on_switch_to_text(); void on_chat_hide(); void on_chat_show(); // Config Config& m_config; GtkSourceLanguageManager* m_lang_manager; FileChooser& m_file_chooser; Preferences& m_preferences; CertificateManager& m_cert_manager; ConnectionManager m_connection_manager; // GUI Gtk::Grid m_grid; Gtk::HPaned m_paned; Gtk::VPaned m_chat_paned; Folder m_text_folder; Folder m_chat_folder; StatusBar m_statusbar; Toolbar m_toolbar; Browser m_browser; ClosableFrame m_chat_frame; // Functionality WindowActions m_actions; DocumentInfoStorage m_info_storage; KnownHostStorage m_host_storage; FolderManager m_folder_manager; Operations m_operations; BrowserCommands m_browser_commands; BrowserContextCommands m_browser_context_commands; // TODO: This setup is a bit awkward. We need the auth commands to // provide a SASL context to the self hoster. // A better solution would be to have a new class, say AuthManager, // which creates the SASL context, and the auth manager is passed to // both connection manager and self hoster. The new class would not // do any UI, but it would do password checking from remote. For the // UI it would provide a hook which allows m_auth_commands to hook in. // This would also get rid of the ugly // connection_manager.set_sasl_context() call, since the connection // manager would then set the SASL context by itself. AuthCommands m_auth_commands; SelfHoster m_self_hoster; AutosaveCommands m_autosave_commands; SubscriptionCommands m_subscription_commands; SynchronizationCommands m_synchronization_commands; UserJoinCommands m_user_join_commands; FolderCommands m_text_folder_commands; FolderCommands m_chat_folder_commands; FileCommands m_file_commands; EditCommands m_edit_commands; ViewCommands m_view_commands; TitleBar m_title_bar; // Dialogs std::unique_ptr m_initial_dlg; }; } #endif // _GOBBY_WINDOW_HPP_ configure.ac000066400000000000000000000101231304552132100133350ustar00rootroot00000000000000# -*- Autoconf -*- # Process this file with autoconf to produce a configure script. AC_PREREQ(2.59) AC_INIT([gobby], [0.6.0], [crew@0x539.de]) AM_INIT_AUTOMAKE(1.9 check-news foreign subdir-objects) m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES]) AC_CONFIG_SRCDIR([code/window.hpp]) AC_CONFIG_HEADER([code/features.hpp]) # Extract host information. AC_CANONICAL_HOST # Checks for programs. AC_PROG_CXX AC_PROG_CC AC_PROG_CPP AC_PROG_INSTALL AC_PROG_LN_S AC_PROG_MAKE_SET AC_PROG_RANLIB AM_PROG_CC_C_O AC_CACHE_SAVE # Recent versions of glib/gtkmm need c++11 support AX_CXX_COMPILE_STDCXX_11 ################################### # Check for Win32 ################################### AC_MSG_CHECKING([whether to enable WIN32 specific flags]) case "$host_os" in *mingw*) win32=true AC_MSG_RESULT([yes]) AC_CHECK_TOOL(WINDRES, windres) ;; *) win32=false AC_MSG_RESULT([no]) ;; esac AM_CONDITIONAL(WIN32, test x$win32 = xtrue) required_libs="libxml++-2.6 glibmm-2.4 >= 2.39.93 giomm-2.4 >= 2.39.93 libgsasl >= 0.2.21 gnutls >= 3.0.20 gtkmm-3.0 >= 3.6.0 gtksourceview-3.0 >= 3.0.0" ################################### # Check for libraries. ################################### PKG_CHECK_MODULES([gobby], [$required_libs]) PKG_CHECK_MODULES([infinote], [libinfinity-0.7 libinftext-0.7 libinfgtk-0.7 libinftextgtk-0.7]) AC_CACHE_SAVE ################################### # Glib i18n/gettext support. ################################### ALL_LINGUAS="`grep -v '^#' "$srcdir/po/LINGUAS" | tr '\n' ' '`" AM_GLIB_GNU_GETTEXT AM_GLIB_DEFINE_LOCALEDIR(GOBBY_LOCALEDIR) IT_PROG_INTLTOOL([0.35.0]) GETTEXT_PACKAGE=gobby05 AC_SUBST(GETTEXT_PACKAGE) AC_DEFINE_UNQUOTED([GETTEXT_PACKAGE], ["${GETTEXT_PACKAGE}"], [gettext domain]) ################################### # Help file building ################################### AC_ARG_ENABLE([docs], AS_HELP_STRING([--enable-docs], [Builds and installs the documentation [[default=auto]]]), [enable_docs=$withval], [enable_docs=auto]) if test "x$enable_docs" = "xauto"; then GNOME_DOC_INIT([0.9.0], [AC_DEFINE([HAVE_GNOME_DOC_UTILS], [], [Whether program help is enabled])], [AC_MSG_WARN([[gnome-doc-utils not found: documentation will not be built.]])]) elif test "x$enable_docs" = "xyes"; then GNOME_DOC_INIT([0.9.0], [AC_DEFINE([HAVE_GNOME_DOC_UTILS], [], [Whether program help is enabled])], [AC_MSG_ERROR([[gnome-doc-utils not found]])]) else AM_CONDITIONAL([ENABLE_SK], false) AM_CONDITIONAL([HAVE_GNOME_DOC_UTILS], false) fi ################################### # GSettings ################################### GLIB_GSETTINGS ################################### # Output ################################### AC_OUTPUT([ Makefile m4/Makefile po/Makefile.in icons/Makefile icons/hicolor/Makefile icons/hicolor/48x48/Makefile icons/hicolor/48x48/apps/Makefile icons/hicolor/48x48/actions/Makefile icons/hicolor/48x48/status/Makefile icons/hicolor/scalable/Makefile icons/hicolor/scalable/apps/Makefile icons/hicolor/scalable/actions/Makefile icons/hicolor/scalable/status/Makefile icons/HighContrastLargePrint/Makefile icons/HighContrastLargePrint/48x48/Makefile icons/HighContrastLargePrint/48x48/apps/Makefile icons/HighContrastLargePrint/48x48/actions/Makefile icons/HighContrastLargePrint/scalable/Makefile icons/HighContrastLargePrint/scalable/apps/Makefile icons/HighContrastLargePrint/scalable/actions/Makefile icons/HighContrastLargePrintInverse/Makefile icons/HighContrastLargePrintInverse/48x48/Makefile icons/HighContrastLargePrintInverse/48x48/apps/Makefile icons/HighContrastLargePrintInverse/48x48/actions/Makefile icons/HighContrastLargePrintInverse/scalable/Makefile icons/HighContrastLargePrintInverse/scalable/apps/Makefile icons/HighContrastLargePrintInverse/scalable/actions/Makefile help/Makefile de.0x539.gobby.gschema.xml ]) # vim:set et sw=2 ts=2: de.0x539.gobby.gschema.xml.in000066400000000000000000000373121304552132100160750ustar00rootroot00000000000000 'both' Toolbar Style Specifies how to display the tool bar. 'Monospace 12' Font The font that is used in the main editing area. 'classic' Style Scheme Specifies the GtkSourceView style scheme to be used for the main editing area. true Show Toolbar Whether or not to display the tool bar. true Show Status Bar Whether or not to display the status bar. true Show Document Browser Whether or not to display the document browser. true Show Chat Whether or not to show the chat pane. true Show Document User List Whether or not to show the list of participants in the currently open document. true Show Chat User List Whether or not to show the list of users currently participating in the chat. 8 Tab Width Number of spaces equivalent to one tabstop character. false Insert Spaces Instead of Tabs When pressing the tab key, insert the equivalent number of spaces instead of a tabstop character. true Automatic Indentation When inserting a new line, automatically indent the new line to the same level as the previous line. true Smart Home/End Keys When this is enabled, pressing the home key at the beginning of the line moves to the first non-whitespace character on that line. Same with the end key and the last non-whitespace character on the line. false Autosave Enabled If this is on, a copy of every open document is saved to disk periodically. This option only takes effect for documents that have been saved manually once before, such that a path where to store the document to is known. 10 Autosave Interval If autosave is enabled, this specifies the interval in milliseconds within which each document is saved to disk. (['enabled', 'time', 'interval'], false, 60, 5) Keepalive Settings Settings for sending keepalive probes to detect whether the connection is still alive. This is a tuple of 4 values. The first is an array of strings which can be either 'enabled', 'time', or 'interval'. The following three values are known as 'enabled', 'time' and 'interval', and they are only used if the corresponding flag is set in the first value, otherwise the system default settings are taken. The second value ('enabled') specifies whether keepalive probes should be sent or not, the thirh value ('time') specifies the time in seconds the connection has to be idle before sending the first keepalive probe, and the fourth value ('interval') specifies the time in seconds to send subsequent keepalive probes. true Trust the Default System CAs If true, trust the default system CAs. It is platform dependent where this CAs are obtained from, but on a Linux system it is typically from the file '/etc/ssl/certs/ca-certificates.crt'. '' Additionally Trusted CAs If this is not empty, specifies a file with additional trusted CA certificates in PEM format. If 'use-system-trust' is disabled, this is the only source of trusted CAs. 'prefer-tls' Security Policy Specifies whether transport layer security (TLS) should be used or not, also depending on whether the server offers this or not. true Authentication Enabled If this is true, the local computer authenticates itself with a certificate to remote hosts that support TLS. The certificate in specified in 'certificate-file' is used and acts either as a server certificate (for incoming connections) or a client certificate (for outgoing connections). '' Certificate A X.509 certificate (possibly a certificate chain), in PEM format, to be presented to the remote host for authentication of a TLS connection. '' Private Key The private key, in PEM format, for the certificate given in 'certificate-file'. The private key can, but does not have to be, in the same file. '' Name The name of the local user, as shown to other participants. If this is the empty string, Gobby will default to the name of the current unix user. -1 User Color A value between 0 and 1, specifying the color with which text that the local user writes is branded. If -1, Gobby will choose a random color. 1.0 User Color Transparency The transparency with which user colors are drawn. A value of 1.0 means fully opaque, while 0.0 is fully transparent (no user colors drawn). true Show Remote Cursors Whether or not to show the insertion cursors of remote users. true Show Remote Selections Whether or not to highlight text that is currently selected by remote users. true Show Remote Current Lines Whether to highlight the line in which remote users have the insertion cursor at the moment. true Show Remote Cursor Positions Whether to indicate the cursor position of remote users in the scrollbar. true Allow Remote Access Allow remote users to connect to this computer and modify documents stored on this computer. true Require Password Whether to ask for a password before allowing remote users to connect to this computer. '' Password The password that needs to be entered before being able to connect to this computer. This value is only used if the 'require-password' option is set. 6523 Port Number The TCP port number to listen on when allowing remote users to connect. true Save Local Documents Whether to save documents created on this computer so that when closing Gobby and opening it again the documents remain available. '' Host Directory The directory in which to store documents saved on this computer. This option only has an effect if the 'keep-local-documents' option is set. If this is the empty string, Gobby chooses a location within its configuration directory. 'word-char' Wrap Mode Specifies whether and how to wrap lines that do not fit on a single line of the screen. true Display Line Numbers Whether to display line numbers at the left hand side of the main editing area. true Highlight Current Line Whether to highlight the line that the insertion cursor is currently in. true Display Margin Whether to display a margin on the right hand side of the editing area, beyond a fixed number of fixed-width characters. 80 Margin Position The number of characters at which to display the right margin. This option only has any effect if the 'margin-display' option is set. true Highlight Matching Brackets Whether to highlight matching brackets when the insertion cursor is currently at either an opening or a closing bracket. [] Draw Spaces Whether to draw any whitespace, and if so, what kind of whitespace. 150 Document User List Width Width, in pixels, of the user list in the main editing area. 150 Chat User List Width Width, in pixels, of the user list in the chat area. false Run Whether Gobby has been run for the first time. gobby-0.5.1000066400000000000000000000017031304552132100125370ustar00rootroot00000000000000.TH GOBBY 1 "August 17, 2006" .\" Please adjust this date whenever revising the manpage. .\" For manpage-specific macros: see man(7). .SH NAME .B gobby \- a collaborative text editor .SH SYNOPSIS .B gobby .RI [ options ] .RI [ file(s) ] .SH DESCRIPTION .B gobby is an editor which allows one to edit text documents and source files collaboratively over a network. Changes to the documents are synchronised instantly to the other clients. .PP .\".SH OPTIONS .\".TP .\"\fB\-j\fR, \fB\-\-join\fR=\fIHOST\fR:\fIPORT\fR .\"Joins the obby session on the specified host. .\".TP .\"\fB\-n\fR, \fB\-\-new\-instance\fR .\"Forces a new instance of Gobby to be created. .\".TP .\".B file(s) .\"Files passed on the command line will be opened within Gobby. If there is .\"already a Gobby instance running they will be opened therein, otherwise .\"a new hosted session will be created automatically. .PP .SH AUTHOR .B gobby was written by the 0x539 dev group . gobby-0.5.appdata.xml000066400000000000000000000020521304552132100146060ustar00rootroot00000000000000 gobby-0.5.desktop CC0 Gobby 0.5 pre-release

Gobby is a free collaborative editor supporting multiple documents in one session and a multi-user chat. It runs on Microsoft Windows, Mac OS X, Linux and other Unix-like platforms.

It uses GTK+ 2.6 as its windowing toolkit and thus integrates nicely into the GNOME desktop environment.

This is the latest experimental version leading up to the Gobby 0.5 release.

http://gobby.0x539.de/ http://gobby.0x539.de/screenshots/gobby-0.4.90-linux.png http://gobby.0x539.de/screenshots/gobby-0.4.5-linux.png obby-users(@)list.0x539.de HiDpiIcon ModernToolkit
gobby-0.5.desktop.in000066400000000000000000000004741304552132100144610ustar00rootroot00000000000000[Desktop Entry] _Name=Gobby _GenericName=Collaborative Editor _X-GNOME-FullName=Gobby Collaborative Editor (0.5) _Comment=Edit text files collaboratively Exec=gobby-0.5 %F Terminal=false Type=Application Icon=gobby-0.5 Categories=TextEditor;Network; StartupNotify=true MimeType=text/plain;x-scheme-handler/infinote; gobby-0.5.xpm000066400000000000000000000176711304552132100132160ustar00rootroot00000000000000/* XPM */ static char * gobby_0_5_xpm[] = { "32 32 368 2", " c None", ". c #404040", "+ c #666666", "@ c #646464", "# c #585858", "$ c #4E4E4E", "% c #474747", "& c #464646", "* c #585658", "= c #909090", "- c #EDEDED", "; c #F4F4F4", "> c #FEFEFE", ", c #FDFDFD", "' c #FCFCFC", ") c #FBFBFB", "! c #FAFAF9", "~ c #F9F9F8", "{ c #F8F8F7", "] c #F7F7F6", "^ c #F6F6F5", "/ c #F5F5F4", "( c #F3F3F3", "_ c #BEBBBE", ": c #676667", "< c #551B13", "[ c #814D35", "} c #632F22", "| c #8F8F8F", "1 c #F1F1F1", "2 c #FAFAFA", "3 c #F9F9F9", "4 c #F8F8F8", "5 c #F7F7F7", "6 c #F6F6F6", "7 c #F4F4F3", "8 c #F3F3F2", "9 c #C4C2C3", "0 c #E0DFE0", "a c #5C5B5C", "b c #A55B50", "c c #5E281B", "d c #704B35", "e c #763E2B", "f c #DBDBDB", "g c #D8D8D8", "h c #D7D7D6", "i c #D6D6D5", "j c #D5D5D4", "k c #D4D4D3", "l c #D3D3D2", "m c #D9D9D8", "n c #F2F2F2", "o c #C1BFC1", "p c #C8C7C7", "q c #703F3A", "r c #AC5547", "s c #922B1C", "t c #612016", "u c #5F261A", "v c #080503", "w c #110E0A", "x c #1E1C19", "y c #8A8A8A", "z c #F5F5F5", "A c #E8E8E8", "B c #E7E7E7", "C c #E6E6E6", "D c #E5E5E5", "E c #E4E4E3", "F c #E3E3E2", "G c #E2E2E1", "H c #EBEBEA", "I c #ABAAAB", "J c #ACAAAB", "K c #A0999B", "L c #99574F", "M c #9B3524", "N c #932D1B", "O c #812719", "P c #60160E", "Q c #000000", "R c #020101", "S c #5C5C5B", "T c #DBDBDA", "U c #DADADA", "V c #DADAD9", "W c #D8D8D7", "X c #D6D6D6", "Y c #DDDDDD", "Z c #EAEAEA", "` c #F0F0EF", " . c #E7E7E6", ".. c #9F7873", "+. c #AF5C4D", "@. c #962E1C", "#. c #8E2B1A", "$. c #772314", "%. c #020201", "&. c #040301", "*. c #EEEEEE", "=. c #D9D9D9", "-. c #E5E5E3", ";. c #9B635B", ">. c #A03C2B", ",. c #942D1C", "'. c #832819", "). c #5B130C", "!. c #533B13", "~. c #452F0F", "{. c #36230A", "]. c #3A2309", "^. c #44290B", "/. c #A4A4A3", "(. c #EAEAE9", "_. c #D3D3D3", ":. c #D1D1D1", "<. c #D0D0D0", "[. c #CFCFCF", "}. c #AF9593", "|. c #AD5D50", "1. c #972E1C", "2. c #8F2C1A", "3. c #722014", "4. c #D8942E", "5. c #DF922B", "6. c #D98624", "7. c #C4751D", "8. c #6A4F2F", "9. c #757162", "0. c #56544D", "a. c #BCBCBC", "b. c #D5D5D5", "c. c #D2D2D2", "d. c #D2D2D1", "e. c #D1D1D0", "f. c #E5E5E4", "g. c #EEEEED", "h. c #9C6963", "i. c #A54635", "j. c #952D1C", "k. c #842819", "l. c #5A120B", "m. c #DD9029", "n. c #D98825", "o. c #B56D1C", "p. c #948E7B", "q. c #E9E0C6", "r. c #FAF1D3", "s. c #EFE6C7", "t. c #AFAA98", "u. c #C4C4C3", "v. c #CECECD", "w. c #CDCDCC", "x. c #CDC1C0", "y. c #A45D52", "z. c #982F1D", "A. c #902C1B", "B. c #6D1C12", "C. c #130C03", "D. c #D78422", "E. c #D57F20", "F. c #736F61", "G. c #F5EDD1", "H. c #FAF3DD", "I. c #FAF2D6", "J. c #EAE3CC", "K. c #79766E", "L. c #D0D0CF", "M. c #CFCFCE", "N. c #E0E0DF", "O. c #EFEFEE", "P. c #9F7471", "Q. c #AA4E3F", "R. c #862919", "S. c #500E09", "T. c #A9651A", "U. c #2D1A07", "V. c #D6D0BA", "W. c #AEA999", "X. c #B5B0A0", "Y. c #BBB6A7", "Z. c #B5AE9D", "`. c #A19B8A", " + c #A9A491", ".+ c #E8E6E5", "++ c #9E6058", "@+ c #9C3220", "#+ c #912C1B", "$+ c #6C271E", "%+ c #212932", "&+ c #4C3B2A", "*+ c #3E2509", "=+ c #EBE3CD", "-+ c #B0812D", ";+ c #DE9F34", ">+ c #D2912E", ",+ c #E0942C", "'+ c #895518", ")+ c #BBB5A1", "!+ c #CBCBCA", "~+ c #AA8A87", "{+ c #AB5143", "]+ c #982E1C", "^+ c #852818", "/+ c #694643", "(+ c #151C22", "_+ c #485B6D", ":+ c #7694B4", "<+ c #6F859A", "[+ c #4B443C", "}+ c #EFE9D2", "|+ c #B17E29", "1+ c #E6A033", "2+ c #E2982E", "3+ c #DD8F29", "4+ c #835117", "5+ c #B9B3A0", "6+ c #C5C5C5", "7+ c #CCCCCC", "8+ c #9C655E", "9+ c #9F3625", "0+ c #912D1B", "a+ c #7B3C35", "b+ c #807E7D", "c+ c #47586B", "d+ c #728FAE", "e+ c #708EAC", "f+ c #6E8BA8", "g+ c #868B8F", "h+ c #ECE4CE", "i+ c #8E6322", "j+ c #DE912A", "k+ c #DA8825", "l+ c #5F3F1D", "m+ c #BAB5A1", "n+ c #A99796", "o+ c #A45144", "p+ c #992F1C", "q+ c #84281A", "r+ c #A18583", "s+ c #818181", "t+ c #232A30", "u+ c #6C87A4", "v+ c #6B87A4", "w+ c #6A86A2", "x+ c #6884A0", "y+ c #717F8C", "z+ c #DBD4BE", "A+ c #4C412A", "B+ c #DC8E29", "C+ c #DA8926", "D+ c #C8791F", "E+ c #36331F", "F+ c #AEA996", "G+ c #9B9B9B", "H+ c #956660", "I+ c #A33D2C", "J+ c #804740", "K+ c #2D343C", "L+ c #657F9A", "M+ c #647E9A", "N+ c #637D98", "O+ c #53697C", "P+ c #4F5E56", "Q+ c #8D887A", "R+ c #697560", "S+ c #5F552A", "T+ c #644B1B", "U+ c #4D5828", "V+ c #556B4C", "W+ c #606754", "X+ c #2B412B", "Y+ c #C7C8C7", "Z+ c #EBEAE8", "`+ c #8A4439", " @ c #9B2F1D", ".@ c #822B1D", "+@ c #B5A09D", "@@ c #DCDCDB", "#@ c #2D363F", "$@ c #5E7690", "%@ c #5D768F", "&@ c #596E85", "*@ c #47594B", "=@ c #93C393", "-@ c #6E976E", ";@ c #6FA46F", ">@ c #6AAC6A", ",@ c #57A257", "'@ c #439743", ")@ c #2A7E2A", "!@ c #2E882E", "~@ c #2E892E", "{@ c #374B37", "]@ c #DED9D3", "^@ c #905943", "/@ c #75291C", "(@ c #7E4942", "_@ c #E3E3E1", ":@ c #24282A", "<@ c #252C32", "[@ c #374553", "}@ c #3B4848", "|@ c #6AA36A", "1@ c #70AF70", "2@ c #67AB67", "3@ c #5CA55C", "4@ c #4D9D4D", "5@ c #3E943E", "6@ c #2F8C2F", "7@ c #286628", "8@ c #969795", "9@ c #CFC3B7", "0@ c #A07559", "a@ c #95725A", "b@ c #D9D3CD", "c@ c #E1E1E0", "d@ c #324332", "e@ c #4E9D4E", "f@ c #4A9B4A", "g@ c #449744", "h@ c #3B923B", "i@ c #2B802B", "j@ c #676F67", "k@ c #96877A", "l@ c #AB9380", "m@ c #E8E8E6", "n@ c #E9E9E8", "o@ c #233823", "p@ c #2B812B", "q@ c #287328", "r@ c #5F685F", "s@ c #CBCBC9", "t@ c #C6C6C5", "u@ c #CACAC9", "v@ c #D2D2D0", "w@ c #E0E0E0", "x@ c #323E32", "y@ c #273F27", "z@ c #294929", "A@ c #2A4D2A", "B@ c #2A542A", "C@ c #295829", "D@ c #295129", "E@ c #2B4E2B", "F@ c #345134", "G@ c #405440", "H@ c #676F66", "I@ c #B4B4B4", "J@ c #C2C2C1", "K@ c #B3B3B2", "L@ c #AEAEAC", "M@ c #A9A9A8", "N@ c #6A6A6A", "O@ c #616161", "P@ c #898989", "Q@ c #828282", "R@ c #858585", "S@ c #888888", "T@ c #878787", "U@ c #848484", "V@ c #848483", "W@ c #838383", "X@ c #838382", "Y@ c #7E7E7E", "Z@ c #727272", "`@ c #4A4A49", " ", " ", " ", " . + @ # $ % % % % % % % & & * ", " = - ; > , ' ) ! ~ { ] ^ / ( _ : < [ } ", " | 1 ( , ' ) 2 3 4 5 6 / 7 8 9 0 a b c d e ", " | - ( f g g h i j k l m ( n o / p q r s t u ", " v w x y ' z A B C C D E F G H I J K L M N O P ", " Q Q Q Q R S z F T U V m W X Y Z ` ...+.@.#.$. ", " %.&.Q Q Q Q Q *.*.U =.=.W h i F A A -.;.>.,.'.). ", " Q !.~.{.].^.Q /.(.i j _.l :.<.[.j W }.|.1.2.3. ", " Q 4.5.6.7.8.9.0.a.X b.k l c.d.e.f.g.h.i.j.k.l. ", " Q m.n.o.p.q.r.s.t.u.c.c.:.<.[.v.w.x.y.z.A.B. ", " C.D.E.F.G.H.H.I.J.K.c.d.e.L.M.N.O.P.Q.1.R.S. ", " T.U.V.W.X.Y.Z.`. +c.:.<.[.v.w..+++@+#+$+ ", " %+&+*+=+-+;+>+,+'+)+!+e.L.M.v.=.~+{+]+^+/+ ", " (+_+:+<+[+}+|+1+2+3+4+5+6+<.[.v.w.7+8+9+0+a+b+ ", " c+d+e+f+g+h+i+2+j+k+l+m+M.L.M.v.w.n+o+p+q+r+s+ ", "t+u+v+w+x+y+z+A+B+C+D+E+F+G+M.v.w.7+H+I+N J+h s+ ", "K+L+M+N+O+P+Q+R+S+T+U+V+W+X+Y+` O.Z+`+ @.@+@@@s+ ", "#@$@%@&@*@=@-@;@>@,@'@)@!@~@{@i g.]@^@/@(@_@<.s+ ", ":@<@[@}@|@1@2@3@4@5@6@6@6@6@7@8@H 9@0@a@b@G c@s+ ", " # d@e@f@g@h@6@6@6@6@6@6@i@j@F k@l@m@n@G h s+ ", " o@p@6@6@6@6@6@6@6@6@6@q@r@N.-.s@t@u@v@w@s+ ", " x@y@z@A@B@C@D@E@F@G@H@I@(.H G J@K@L@M@N@ ", " O@P@Q@s+R@S@T@R@U@U@V@W@W@X@Y@Z@+ `@ ", " ", " ", " ", " ", " ", " "}; help/000077500000000000000000000000001304552132100120025ustar00rootroot00000000000000help/.gitignore000066400000000000000000000000061304552132100137660ustar00rootroot00000000000000*.omf help/C/000077500000000000000000000000001304552132100121645ustar00rootroot00000000000000help/C/figures/000077500000000000000000000000001304552132100136305ustar00rootroot00000000000000help/C/figures/main_window.png000066400000000000000000000550071304552132100166600ustar00rootroot00000000000000PNG  IHDR*u6fQsBIT|dtEXtSoftwaregnome-screenshot> IDATxw]e96w$^!?V]uv]-PEW]]XqQA)R,$RH/3d~i?nɝɔ;)w&5̹<>Џ\oB!B{z];Qǵd'2_+{](sR'B!8ؖN<}.~S!`) Wڷ] 3Juc&#^!B! @qQnTO=|domdĦX!BI'r?zѼXlʽO8ӲI;%HB!8詽\v߰zQ]sÿΝr1u$2&N^h_F&E&9+B!Dd[+4ҽ.14)4,X²miZXMȫ1#-\NuT/bd~ ţ2Bҙa3.meO85Hs5*d˲ӉAR{g8uh2,+XOxL+U ..9=rcB!yԮyg `$Hwc߃2?3Gm>!C- nعrP%-fzaˁ~OY,Y1zL"d;[E-ckT740 ˲mp8d9#l}J/^c !8f䡻ɊO=ᅥz ^>wK]Ϧtr}F#kzߐ0\JP׎q_Ŷ_zŲ)n+thFX<ө yGioocwS UյضOFÒMBptwup* <IoIH̽dݻ/߬o~LOos.%罏p/W4;ay\rCk|T-{1>9M-ʕǦWQM c&c>WidU57\1oVnb{}Sn@2mMp'nųOYeYD[,7kfx\Bx*ꩩũL ?~.|7tiW&;|m],wpVnGY^.X;@}t6OVcm)mD֍_v9ip`v^"+fmDE!Ni"hp"[2`뉥}?ᮧ2N;}ըЫ@ޟ^O/>ka2#=$ƶmlf$vڋ߉f!ZbY岫'.FTơX83ɾaq8ztip;YT U i9Dڰh.}gƜeq;ChNnOcv{B>{qኚ3φh箧3 ws| u@w2ݻbY>$<,>|J"#;Pµ z5C$TL;fޖWǜ=~Wz@K.pP)SQQTlr?@<s/~NJph8%Y BnwH`&*R.f/e}Ҧ 68T[:!iu.i anUZ]|Bux!n[P'`c !D+%+QrtLz}c)iFefr Xk1-;7 R{GӀ~IW\3EjO B1 UU0j>?5$p^F>)7<#{6qMCSaN4BŲ>˟*끾MJhUJ+\膍!ks1Jw"јUeNN !D_}o3|ר.w^6zh Z<ӽg㨮T!}6qˆJs\2z!2&zc_B!Ds󭉅B c4 =~Ec$)B!pٗB!|8*^h3۶B!Bh8#Mxnw(d!B!P'KB!b;PK)B!Bπ*!B!KK/!B!D_`7Fo7UvB!1@ӯT,YɮZ>}}=! c܏!BQ>FeҕL۵uvm{2Ʃ|_zi]4}tYC~oXwO?8."(}m7Fe/!BQގʮmKPMO)Ki_?'"u4cބB!D9>.Xik:''SHB2 ٛXz g;~^]@y#wcYV4:?|ڿn|{]pܵ0}]+F%ݥξ]twǜ~yXOf2̸W!B~۶Khſe6z^?\n7_7K^Hӓ淿CGg'y<<~Ӻs&vnέF%m˖.aVc#?cjsfZwZ6l| ۶9o2X۶Ya#眽v̷pmᥗo/Ovq ?#?#?#?#?@e8.;`Anܧi7|ǟ|x0hz#qg>Euu55O꺇rG?^~ӻ?mugeƗxv{dug5joݷP]]ō?بW!BL~Q)]GZ۸(elAg66꺇rSQUoOg2\I`ukxn{w.^{m_틣iq2}[g̠8S)B!'e8O^[S}9uu#^HPS]&fϚ:ʻלr7}cSzllw^s9|<ԟ5s&g?ro/MTWUB!Әt{jn2pm~?tvv|+v8V!B}_kGk9sg?: n򓏙c˫qo܋J |>li]WD"YhP7Wt 罴|] \q٥|k*Y;1ĥۣ򖓹&Op٥QNB!̔ukϰ~[ tj$E+V B!t{ iGd{;7^a@ɛt >4UJad.B!'UnrKO_垾L4 !B@Ev11&B!5_B!B1VFE!BQvFaxb!B!]*t!B!NP .~Uk!B!@"9fZJ3 !B!xQ':B!Bї*B!B#B!H"B!(;!B!ʎ*B!B#B!EgWWI.?oS3Z;:;b~Jx܃. ֶAkDT*E n:M$LqqM!!Fbx|xܚpR.^CUUl&~%{5 W-nøS~6)vmMx< ]43B ۸֨$ ,pr OOtttt:NmMx&qLM^^CcnSU>H-_ }B?7z}$f0MlFs8Bt3_WwOɧaX,UH ݩPrS7mmqd2NgaqlذS#:@3˲un(t:9t0 '\6n͘A,²}XL v ѼeYXEKK i 4vjʾL&"]]A0wq4\#Ye˘j@O%@O'ad2TU%t:qMQTU6(EQ*}ౝ'ӰŅcH҃vDN$V<9^s"gG à"{[M#äit]rXv׶ RBan[1){_;NJ0sxltb:aD42V>PUޣs(A*~24iJ&.PɎॐL$phdv+0z@wl@Q.(NG WDzаPl3P(ʍyo:T ]t-3edtˁg0sCk[t8q:J:'֕N %"62 iӝW5CNpedO 5@N<P0H$.aiA0ĶmĔLeӅ;r dLB"HI%ػh4 Ͳf͛ˡCTh}PTl0ٳh\8f .&sMC_i'~inX,V}0 t:mYTUWaµ%9x}^tۦ2Ť4 (c6a`+RM|!H x`0ib:P{H{};FMsYɾaB3ⴍ$ʗjV BӴnZ}m9Jyդ˧WU5,  oI4M,>a]7ضuX6dH8tK0r#r0z:MW{;sCi4U8o?-l [\ΊSQ*,&0~f777w^b3Xt锿&D4~{p1 v{HSI| XõiSI,B\et&_`1 ۲r /?yk)ZϱAQ˺0_ܔx-l֢6i[UUKIJ#)ٴVA @U4M;!n=]݄B!55r8N6"jSg6J"GuVz _yj:&8o8Ȕۙ~s؊|{<LӤxEUe%MMM̚5Rh.lwq"@ݻwB]!Xmep 0t۶Q_J:DMTU䆩-\r>/=s}TtH,ߎ JnO  e`[B0tNtt: BwcaaP}*Ɠ/ ycfTU% N èBeUd@0;0M`0x&(`yUVհ{׎^R>K19-aP_퇺{7B0v' T*dT*IL:D`:x O%\nP,_h:}RW} ѿ6!Q)jvN>h8ZP3iSHB:{fN$ٱ(ׇ=jV4 K8TI$!vO7Wm}{w" e_fۤR) f6( g6ky=++Q]D9{vM 5qj*u\Q4U%/0#O3)~gLɶm2μ`^E Ù&].xbDroV_@( +*m۶q{={Y^9V+vݾuɽ. ψA'lԉ3rM!N~ӑ0~ i=d2H`yݞBd?@gggaeyޗbjr_i6V?#2 >;}N@Bsmmm3yvSM =/NO*(QSf,kR**BQyEFPٍO h;ӛ۷t v2'h`oRu8W˕{aEM^NpQ[WKMmM!0WcgKz\V-AJ~H}Nl8+.4/X]rQe)b*@@ӲsKUjɮ)8DQ6 8c8*_r|Y l rhd$Ge`|&{KQ1dɦ !B!NxQf~N˜Bo㝯ڎSu&e(\0ISS=BLEQP-s2kc9?1i:Vrfsr&{"]*h9@hS6> Cg7h=L:BshTT0c\յ 1;R5A0\ɂ+N)wVz:1Mƹ ^9. D/?ϜE8p8]4/3/ھ%<>?kι` 4hkib7ig&:bw2q@3:;)giLxms]֜#-1}Y$ۉNXju*hiX}y=>b=qvovlˢ+߂쓜vE8Fu{SϺtLfCW˶عemGqhfYekfZa: BouӦs+Dw)a7=oVJ:#݅,]u*.=I{~Ziÿ,ȗ`Me964֙N&yuӜz83BQ51|gOǑ~k.VvxT"aּEL1{8}-{mH㙯chHCc/˲طs+Gb%4ΙL@L1/ 8].,YWף f{wpow~/}PpMtMsNf4-{jP5?hc%q2D5 4}K" +ߋ 4 a[c}1̘o6N7lyu= 'eSzxu=3.«% 5سMm :{}No~rLf;gҜwsW][ɤS{;`mc:$@s8<'[WwG3/!N%m lЉG ;\P(,]}חE{RT~8s"ݍ3WؿMf^@U݌?,+y{Z>,XKAι䝤I\@+>&Vn*_CCbý^k7#m\t߁ n?زPe5,[}:`xhiޏ#8.ZDUU::ZQjf1Kg+it3iΣ7Nř`1 dcI"M#Չ ,;t<^/;m?=gg70t0ϩk/`4h73fΡ ?ȌYػs Nwv[ٶp%t6`HKW#1{^m=|eş/\BKӾ^xxwt1q=Y7Zt]ǽriRJJُ]P5V Ԭ"^&:t=SY] ~]P5 ˆHW'TpRK9\&xkchW^arpzN^?"2sB\31ƭmּżsT6בIxG!\]7*MxZFD:YpFfE&Ʋ^tU8|l5!Jt{Hƣ1I$VlBQUl*A_zND,mxs+X}&m `Glo%~*BU5z:K6FQ~ PmThӄjG- O>Oz&Exyq5^m8&KF$`g߆rRJJُ]gYȮ-rm̚G}\o9TVqҚuzfp^|e4W3o2]\&xkchW^at8ʤӄGz 2&y &޸*pl{u=oY{~\n7x|ci<^-4gf:z^]1KwEU-~Zc`Ty'b J&p]6dvypl"]h.X3O>?&MY$^4t'j39e4˲Ub:emʉ%yx,=f@vJ{7>wyH%bϓ$Np\SEO™]>供L Z禮~Nz(%OuؿMFg?)ŅW۶12i!k0˪hkai1}gZWJHmi<514?O/M2d;@9FgH>Ѽo/‰VY]ǖW^dڬMM <5uӱLsLof/fM9|1 MiڷN(jv0M3|-4LYnc)VShٴYڎ4ɤmt*m VT:ۦ/Ӵ6g﵌ʚZt84Fl9fMokB<փe[XEoʌ9 z=aL]C#oO06 hNpG>sn2x̞][_p3c.0O bMؖE&b׶Mp:E.z”R`G9 ٽu,]&n6`g RrGi=έ1 MUMo9B?E–WדGqy<֖̚^˙>{[_]+tƅ6i܅>vlD<6xf̚IJ{5wRv‹8w!G YDZmچF>}T@ٿk+} vopM`՚8g;e̚M.?]^gx=w1mѲ {><1 ,F*%OmNdeMP׵z2Xէo~׺ϛ{(5_yGs})z Ckܻ{ ˶=o1^dnymfa΂D5o]7<4 h@q2\&ӏ26^*614x٥\T"^hIC؎4t*' .Q!zh6-h>s.}F&E B#I>P!0<0kFs8HD%HB%()BQ>s 4`!d"Q$PER]*(N!ʃ܌B!BQ TB!eG!B!Dّ@E!BQv$PB!B TB!ewm}m!B!8A-Xv1 T5!D9gO?8D!8qrƹc!(JL't>. !goc(!('௨,i^. !ctBlBUr(BrgB!B TB!eG!B!Dّ@E!BQv$PB!BA'HSS3?$دϙ=sYw B!BF<|t۶ww'zUTVx>D"ɝ?)|Cysp!B!5@%T*Ś5kH&(²ex<(=Ý?1|U\!9wRE3kf#瞳_{ UUǓܲ6wR65dzϽP-ZO?={:+ַ]ÿ??EKK˰_M/oWk>|sO|s7d˖<Ѓ9n7{-xWdkI6db|{?O5}Ӧ<ЃٱumnYl--G-6r9g^_yXy&!y>1*IDATi`?w? z?w(~X LcS)BQN!i~y ömR_|1k׮{D Co 5pMG-|oozB 7}3Zߗoix<\{ǹ577}z~?ǟ[p3ux^ȵlZzPup)~w={sgWչf桇<ǀlrlikmӧ/:MM޷܏^I},NB!FjDai(@4Ŷˆ`]]]$I4TT*i> wtt0k߳f6kY3GC,fG9E^^"֜^ +~.{wn㵗s5nRyZ0ضZ{<|__W^{gm~9.{%d2ip8ضDuz}sYg`Ssf&p`'RT!i-O~syEE7ض|̚9G=^;o{dYak'@Ns0If(ƌ;?|1|?/̞_&n{6}f߽ܪo{nܸ1swg5q՗M89""&rR̽ױgƳ>ve٣st{~T?#Xq笈;gEi{†@A_|wϏ۶E㎋ؾ}{l۶-o7o^{1`6mW׽>9bxwѧw\4˜{ٕW?_0uJu1 S|ؼYώѿ8ohtIQ߉3T?{n?yS?oFoozU8!]g5rıqƟWϻ6F*cnjsL߯y! 8(ʵ{P[6ǖUEշOUwsjѰT;lhÒ /*֒ cБѵ{f+Ư<~9|*lk׮'ƺubݺuѭ[غuk{T-Z}D>nT4YoحG[c vy5gW4oأG8ĦMsΉٳglܸ1:uQWW彑Ԧ!c"n^ -=VTZ}fb#^ #Tt P*@:BHG #Tt P*@:BHG #Tt P*@:BHG #Tt P*@:BHG #Tt P*@:BHG #Tt P*@:BHG #Tt P*@:BHG #Tt P*@:BHG #Tt P*@:BHsl*S-Q*@:BHG #Tt P*@:BHG #Tt P*@:BHG #Tt P*@:BHG #Tt P*@:BHG #Tt P*@:BHG #Tt P*@:BHG #Tt P*@:BHG #Tt P*@:BHG >-:*c&Qu42#Tt P*@:BHGT4T?JMU4T>ɳ f2BICe+jLȢ2|6ʀbAq!a^w};=n3OUjɻ\CkZ:;Z >rh >=gJklϟ)rկgUO_=$Ki8SŵkaY3ϊÆC'~lq4n'nkDDĻ_sI|+_Z㱦; 9vT|ȡm۶uG?ig믿KĐ{7ݝNspcIq'Έ^zF{'kgύ >2.tnn3zx?Xx_cӏۺvhvcҤIO7onӹG=bq»c+#"v<ʸwcGuuu̻Ͽxg{~K?nݺ1Zzqq-Ǻ? z?NK:c%m|X;]CeٲeqM7_]tiӹJ/dVg9c}G׮]͈ŋx$8wѽ1gok=/ozj1 s/8}sfE+.ӟ^={6{_~;}~'w^={/}m1q4~~GmٳG]t[x{J.;*;~pW 1~|߾}b_67׼b9昡qOh1{^=ּbdק5%JF͛ɕOW͋;Q̘>'Έ/̞_&n{6}fNP\~UnxՍquwmJ|[7mw 6lx%oW<=ǜ<~\\qռx6^˯:N9kl@K**72E.={Vɓ#ęg}*=}1Ř1_0uJwƸ'Ĉ_?5ࠃ5,]#G]qUf^}Qƨccs3͟F1-:vІ%?^Z%Ɛ#k+ۿb^[^q]ƈ&x/>\X7ӷ5RZ{GxwPl  P#Tt P*@:BHG4*{':ёkHE8ޙ~mѵGTUՑHE8͆u"t]ʈ&t:R{hgW%ߕfwTFHkڿPC h #Tt P*@[xaխ{Dž >o>^mC42tBq/_@a2q/_@a2t֌{:TkƽZ5UJ Mf-G>Zvhoԙb#˫G&`7VeNwx%UȇcBjׯW73>>*"|`DCD5ώgTWw)bndYQ*o:vІZ[;a4v5IENDB`help/C/figures/preferences_appearance.png000066400000000000000000000424401304552132100210220ustar00rootroot00000000000000PNG  IHDRe 0sBIT|dtEXtSoftwaregnome-screenshot> IDATxw|Se_ini d"D|-  PTTG@EPF6  ]y( %B#g\IS>ߢkd6.\pnjYL};BDD ˝̩3IVh gσ[԰FlMтǝpי91^<\eW{|;.tRzUM{ǹZam'S ),MwDyf0(_X'A Gf-Q. G|2_qh@'8wOKM( U7+n %?BP$e8ggLkNč)^G9Cb;[z~ 5͞ $:8wѫU} p[nR2SNI9q#gna7WymQ]坻i)[^qc ,Gl8jt'Ovmݢ^ ےZ'nO>_{{8v㟡t^l^2/ ݽx>x;fUPť;r.^,fKr% }E0pn65溈 n/Zcd7'X4 d/O;T8 Ï~Rtq,J]%2 v#zx1sG>w6l (X.F{˕Į>T^&j{!ܿY7:xKs*YmEY>2o_$" 9u %99-S!"?:i%Q8NdAb2l荾5w{&}Y~W} N_dr|&1}W+Z!в,5n9z lN_a;{M+Xn7gb4u /I9vU*$,Q̘W{Q%Kn_7DPhn_&Y7nhn ft{SP8Sl0mT ŝĝtT8/xk[Ͽvsd_ln mSчd)ns2!Mp3I)a/:uk8}3g:aYesO&]8_}# 6lpBɂ DAm6*@;$;DHkز1q^l+?ͿY|ý%kdkr@ųwt*Oz x~`$oApG/F]mEҭ.z(O3mL{lc+.GϜf!7?o:#eN'Ŋc][ʷ -w9_ B4 u''[#ޥxZ-jF޶|4T,ęd7J ໷,`xfhTgO'СIJ{p4!珱[&[yey- s:|` .fgkfwnK8y'9Q&]qT#yC.ؿ|:waiEJ1K _Nݬݿbva|OaW?|MֽiL-mѯ`ƦhZ h/KӳeyZ(yO" t`b 9ۮt%v2YW]_Rx'""*W"..J|mą~L;cV*DrJG36OFwgk?ݴ_0QL?@OV %.Q68&BD$\:!""@@𒌝rz,""V],DD$(ED P1BYD e(ED P1BYD e(ED P1BYD e$H<֯^&~׆J`;(Z:U~ gЦʉgΰy c,)Ѹ؜_Ln(_I;nYY/fHĭ9=+nS4q,;ͲהWfL ;KvPvv^KƲ5Vs{nm2cܘ4!n58_ai9!e}&_&-#FBshquB <5fG $9:Βt|*W[ghcb-,+GNYΓlXv-jMk͋u)ظ`YXn;0敱0x|2ϫF_7i.W7,JP+nQo;[7vEfHLLx;vk\ٺ8rcX\>6_δO>J#0ַSv}Ɛٸ,"11L7sK|4:Ukj<{y7~f 1NRX.|g\.III|6c&vyk\om:Ŷ;<~3[mwЦ]qn\閏>GtEGyoXE g߈9̄.7ٴi uq:=Sޥޕװa**VK/ju,~v˘lܼvmZ򉯽߲roWg5Jb)V(m[5hҬ 6Ï?Mw׹|B<--rĉtd̨;/=Xg>T6n){sVf~IoxqfMMk5eYn|k*G໯'XګNVy 6[)}Dỹv,<S޹1We)ŸynjTJl5iԀ?֬Ų>WsI,?дqC؃,-vaMN, :ё 7{^6/JxxqË|˧Ϙȗ_D`OҸqcN' b BXX%=rQQQгl|%"#yi`OW_eЀg,Ahp}{?O?΀~OSHQPNtKZ/`s} +ZbԿW;=N[0]/Ҙ?~׺}FGY/֤aVm1p:̟#5hܰx쁩m_p'unt \Wg^wGKe/Rӭ$&n{Lxeoo> d@Ҵ1чc~6ͫbE ?[Ky^2xv2z O^4:5aXM7aيU)\*+Kf:5Rxi@>І62;~A,J.Yw^:Չ3(={R|(Vf|lJDEy'͐s[[)X Nj^_9]dDwBkmszq;!~;eY޳iفDDl\,%gx~Zs3{xwif.];Ow?/?EEFcǿo۾QժTy7 |iF)+΂X|gY~wƁ_֦wc-tCd誼w]yi86nDٰi /=wwl7hvErr2ۍ>w0 ݷmACGx3;i۪^#3n:?.T ;wߺm4IVͳ4f˲IC9rF eXbQ۴o˲h׺LxF ҿd)->?szi|SҺ4E_gƜY={pM7ÏpCZy[;Pz-{anN*+u_L^\w]ڴTɒ~zǞ˂GJCчy}ʻԨVc{GּڛD>'0y38@F%v_;- 0גG'#N`-s svS-M?x{\26S/9bb(W4u}ث?_}_llu7OnͲE;?t/u{3gxٝW'A{Ufxyi苼4KƁunb77SON;j0yʻ1, {\A'DFki=73;e˔C=7͍Z-]CYn93I]wcǖƼ/}(I/n7k_]-t"u[ݗJVi YwZyU]O}a:}tz֫dq n9}iy-}& ǖll;oʹ7rd(^yE6[=9Iwʆ3j@l6 %no e5泿',)Kќr8Y׬&cˈicN8}2'v!dnhx6mf{(D^wؑBk&wfٺj7Ⱦ' tъ\ ]/SAkJklm7HD$`pgWT(D,"bA""Q(D,"bA""Q(D,"bA""Q(D,"b|WUjd\Dj"_r].۶nJ#:C7-" wߧY6TQ[[f{{̥Zڢ5im>)WAjhtm 'OâiS!|94oE5ŋHkCߠ}6cm[b7x.i,Z?%L5U>l6 g?6[sY_2rtmm۾?n <-׆/fЭ#8N?f_6|-JbE89>mx+^GPPwſ~}&44R_CF)g :%m%J{|!Ϻ?֬eߜ>}˲8zX-.)/^FEE^69?Ok2i«lٰȵhIII|w/i7#GrQ^?]]. *DRR^ JD\;}t',7̝>Fdd}M/i4o֌})^<=Ǐe䘱<ܥQQ|m\ ߖ.h졋o}?Ι]vrzl`q8쎜M:>-ǎtD<vGDҨ- f7k ׇ˕K)[<# ׿$Xb W-YV(r}YaqD`Gu4a[+HJJR(g;+: dvەCI'Y!mc u 9)1611L(M\);E;{; IDATr}P,7)U;t o(̉בh wE`<XV~ɉDfױw׎lˏyYfƎ}sGL(g'N0q,':DhP&vyMW?wrk+g,g|VΌ䜌3g1dpV‹CaYV<{NOSlf|6RJr1XoNI㆞rb*}_.Uf%k~1k6c_өY7nKǰ(ά_΂; +V0xJNXX1noߎϦfT\vd˖?= "j֥Zͺ za X-yW[nks\L}ojD5`\JPr*:.\ίĈQcS5j?k,id_~eT\77ci=tѓ*kSJuڝXJHH\&7f=?|~_1%%&PTROɓX C1slv9 ˰=-Y[]1c&>5cY5jTgG0cLs4vMwB;ƺu8xk׮#!!!ÃbJ9 kWѦUK8S>a$f8p'cY77mU,1쥑 xUrs \j5."&&I_ϝ`ع/_1=l۶~C?>y=ӇM0sg[>1uѓn]vrVXJ 2r+~ڤoX|<h?T6o·sfͪe8NƌeY "fżoDz,_~/f؞+A|9kժVZ_jU5@5M HL}M_B:71r ǎÇIHp0?֍-nϙ-Æ xX s֛rj,fN',+WqM} Y<,aCy~VW_ۧ=o>EJDERP!0ƒYN1T=218[7l3(B sXd}}3wXyd?~1kg_ ϲp`Y0I?WOdט$2>.Zx+Ԯa̜ەLJr_ gz3,v>`g=6RRR<(U"TIIN~: y$)ɉ|3wcG#k׭ca>P2*SV2*8L^/ie11*s[ƒYS&OwgS(X Cy[}g :a2n3g`+66k*Ὰp>L6֝?bE qgۙ ,Ŋq/5)[Xn+ -W!5֓ػw=+V mBҥ8zsS.?e =js߾է?clٳvYKj՘:e2+~a/ š#2SxXx!oZK,_cpݻ}pyg{ZO֭3˯y|ڵǯ5~ĄDy6okrW3ekᮏƒԪY|7=n߮5ǎ#668F2۵7m҈WM,ѯдI#cJk̸ WspBwgcltc^h?W'=۷ Arr2npL 8Aw>4ҫա}[~cq_{a޳g}²,N>MA/21l~/}g{~dm9w{ NwѭSlܼ˲ظi3z>="Ps9=؉76އp&MޯO/iu{GZޑyS޴q#hߦ5eѾmkbh8Pvm:uEt'[t!*רȬדStt-AQ~blތ^}S~cƿ6qcFf8/q] =NڵںX_~" Ѭu{^׫>օ֡KԾ>?͛aY/Gu,fLy웑Ӓs(4>ҍ{tߧ~k͍Z-]CYn93'9yH6 ?ɉ |6|'_~Eطku 94r / }Nӯvg|BE/{lehuF}ѫ_y/~оH^}wPNmʖ-s 2fH""YsLy"L(c]H_ʘpɢ|r}P6Uɚr}PHU,vma\5?m`9~,[NEDr pFfFeN_[ȝ,"fٹk')顤C~&,_+֯ݟQ+aQ$I'ׇnHb4oݎD,,HFl L@3Ne݁38gphNED$K e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD ȰlMOv)"r4 KrfӬeԨí-Z3qWRoЪmԨC;U=n+"r}(0qoۆmVLAmW^Üf2yxo Cw۶n]\iw/ӿ@t:hf̚O&44 }ED ,, p6**@,qdESH!oZ&>ӱ#hIII|w/B k/2t',7̝>Fdd}M/O=[Lyv>'"9PX8پ%qn][G.o""$O_% e(ED P1BYD e(ED P1BYD F?, ˅uۋd͎fwhlݜ8vK%nS<"FohX86Y>]VXJ2tW%KXj͚"(84%'e9x`6WJJNID pPAֽ$rRx#@8\n7]9=tr(eYN"riLL<X>}!lY:[p&b<ʖeNY$u-2 eH׭<X F׭<z/"WO c<-ˢEl߱=;%/-{mE 5˲;n<}>C{Q e4}qEӢi٢g݂?Z UC@ קϧb*~Z8Ob3y{`~0 Zo٣;L}k;S;S%$&0p TYj50IHL𔗫p=~9oҮCGlwſB6X*o3jrM \DB¹w[ǩRT㑮݈'%%2bhԫO7)KIIfԽTp?6ya9wf#28пgy&;e-ٷmӚx֬YeY,_GѦu~v &qP4-~L8٫+WlX6Z2š~{6aX~W+WfsE0in=zҭk֮^KPZFzS>7ٶm;yz{*7o9_f2N'cƎcϵgvjTy^ZUٶ7M e,&dm6(>XSGǺaO;y2l`ΰ!/0g}А:u7LSv$sROvL~MfnxͳTTy⽎I`O47PPA @fz.ٶddqoՉs_9 6SKOtaZiu ѣ,X'OR 44<`$eYҥ "}"00 c+[={V}ר^SR3~[C^bٯ?y9 o<-77`:u z#g>-_."3iDEFd*^w7oQlڼ&M_Y^[Rwkݺejzt과}֌;8bc8no綗SoAl߱dn Θq'..ѯyq: d} yiWx#ǼC;~ѯN=p= >{loA9~lrҽs1mc&ч}0x7MS0b4_1fn@֭I.|rl~+g޽/WqcFzwcZtN>M-yWOO藇ʸ98w?,z'NNaYO|SXGCҽ'111/_'s-e/oYr]ٷ/ӉNwuvsoKs4Pq zIN; COrbMn=p֯JغauNC.Ѿ]۩Ӹ1vH3>NOƩGYZyo=zCwʖ3K[ eF_c{y39j:<ʖea\ܢ58[_hu<hNYD6?G>Z,a>m6;%Jb;(m#we箝,6#Ne# |lZ6φdaQ$I'ׇnHb4oݎD,?."f6 g0AΜN:>lvP9=,;_H$"(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P 6[l9{۝͊Ų %)ÁݞXxdIdg""Ffwph£JekUjcڕڶUw"vZn38eg""1BYD e(ED P1BYD e(ED P1BYD k"brah. l6;qŻS(a͉cXWJv4j| E±eWu^H,\)\e8t_֬XUhּAW?!<̲<}͎+%%8mЄuo ))I,"Y`[|np|_WyeY9=\jAH>`U*H~p w._ݻ_2?f oIwp\ ePNHH q"!O` t#lv, np+ R 4VN`%yGL(ze +"u w'OHZVC@ fqC҂9u6SؖHhZ8AhH}yBY$,~/!1)\hYle)]^Km~P.N:/w `RCutPv;[(lwZn0:2uY(S:qr3}!H~`:JNX Iư@ݻS*-[A=w|n}S:[v%'h">LDDLǓV/..R%zt0bI,E6, p+ߴa:IENDB`help/C/figures/preferences_editor.png000066400000000000000000000656041304552132100202200ustar00rootroot00000000000000PNG  IHDRe 0sBIT|dtEXtSoftwaregnome-screenshot> IDATxwXSoX( VTT{mk[[ϭumZ:ꬳnk{ZuWD$ $J7P l gΜgtqdZM%_A)Q {{Uf@,~?x 3޵<8~^3ܸ06½Ǹo;1Cxrq+7qO0~npxr REmS3=3[ 9ǃ}_%}LeZVC"EXE]/K͏@9KR g7~lZ_"ɬcc_帩?]c%o:ɵ)y7˕bNhOXڧ _p%̞ÇPq:ϟvxnP'w}탲o? _|F}Wyexbhqw?^/Tpvz *?Zlg-7޵>Sœ}=mNuGrausR{ 6?1_wcgwoZaGV8ZM!+_MG)^ʇ={sQJx8%ҀN$Xscdby 0L#[UEr{yJN s۷C>EP EQ翣yٱCVəCv4/յ^(ϩP(+X^,Bjg=3ڕ͒䵇emS3n| ƭ\z3Iʟ0zĄS͑/DCqlYpC9Βi|) yˋ/L27&B_2. mQ{ yh"4͓l +X08s&l8e.AR3Zm.t:ςqS?K+~[A -Y0k\sؑCR]$; 8i~%tzE,k[#Ʒ}8J6qRx_,-yCp-.UMs-Ny̎wn<,zKc%ygytg\(1:|IRl}a~ .QEQwOqk[-ᶂ Ԑ7=^nXV{`oQOȒ+?V'$8ȠJA\IdzQY65&SᱨW͗Gwv]K;oVn&6G!UBA#(T܀".IO^XU;WOr|jP]M0_߷|>g_hv;Z&W\<>+JyN=}-Y,5FNQ75ErC 6G۸~'6oHAwz|ܔURD_ X65&%go) 9lqvrCc8:ͣe ofH¢MevaBݰ}] 2ه9;xUUQ 7i p.ǿA;CͰO閝݆(" dCbՋ < !H'ŠHRB+"IY!$e!"ŠHRB+"IY!$e!"ŠHRB+"IY!$e!"v[$"668(_v2B%Mrtd$]:Mȓ6Ulll\lrStܹ}?M$ԭNzc\.((w8»66668Q|UԎY;Is*BmMˮ_Ne(^,6(6x/=7_V1:кco){4|o\9O*;I9qe*ųimiᄟ#:mlz"Zlj.={VSFiiR)qYSs*LN`V7:Egu1etڜ a\gNKػH*cdNE7A[{4yqq[>C[{Ok)9ޙSPt4. |0M4JV S/퓲5؃ =Vxo~{4.l_k>K6gpaNJI?U%ɭOy͕J#In%n㩺޽ԬۈhN>ӧ䮱(r U⇕k5D7cs3fͥADu|PN>nq&|\VracN 鱄Ȩ(Nb)[oޠM^zo?|h(I%1j4QQ,[NJU0i|6s(XЋ ˶FqO?SƧ49rdsjcƌ-?ƾYnt1ưt͚ǁmEJ9ˎ?I>Lj4lښ-lחԬ5>j#"%Vb͏׸U6Ё5Ac ns#88'=?~°჻q_Ϟ3G8zÇ̙;נ˗]{'i Vʊг{WVVkp\Ѱ~Ok'\X2oMZ;w߿uMZZ;~'''>199~Ayɲq+ZPJ-Ob7Wn?aTVrjл M>>,iy/4lښ2?f]@R>֬8^sszy H|y!W.g6nX<>F36_%VdJc׋_gQAS={|Ejۋ'fzU_l*߸Mk8zh/j,2ϫزq W/2E! "In۶qqqՕ)_L~߶ؙ; L0ܹ]g`p%?48g.IIԼըV.(:>s.#(:Ν@U-6oOڣ(:>Co)I/u:~߳`rsA';GJB=oLJ%mΞ8u a/Ν;i-l% 0xͩo2L2W$**h;scؘ .KiEGnrgH/Ο}BlY#IY$%wٲ3j`57 "NڍhԬ ,iXcؘ  VZY8~4}qD/bc!:*[[Z6666fNaʵ|t9ٳggԮYOha*`\9˿0)7WWў}{0oE~Feɢa*>cc&>fc,^ۥ4[-[qs¸N#I=wܥxoݾ{}R(‰gbl %?|KDep= @ө6[2et̚(|1ej6}6Eїjق/LeLr [4>SY*/_ϒ{yg|GG5Wa58^ɢV4v{n$_^=sg*^_K\}*7wG̰AQ ٲeŋHj6la:msqq! x%bՒ (TQCPQ+T|DEEV4lۊ?loSv F,Ice];u6gpS'=u2@*nőпw1y.,7fh2_j5sgMgF77~ÓPAb8hKZՊ|l%EAqú|{U|&<hUhټ1ʔN=x ԡov͝Ǥq#;FJ&&=pR>EdqWly;3, E.]V"Mx SM3/geJΟr{ӱ{?"##pVE/OZ6o0IUzUSDj9oeh_w()o|7$0pԱ׺k4]Za'ҤQ}4lѨMdUw/l@@F*uc2mر7Ұ^ >魼ЗlUFKɾЗ^7&s\ߖ ~Uj7!;3J'=+e{T*2B93}J٣lg<~\ۻmVmI4SH/їd0MT{732kOk)9/yR™2MDx!kOk){tMBFUQ;fI~<)/L3ϟw)bs)Ļ.$(;pyUI3O 2^V2BX/;[[<{r֍47?B!66Х+JRB+"IY!$e!"ŠHRB+"IY!$e!"ŠHRB+"IY!$e!"ŠHRB+"IY!HNż}(a!ȇܧs ֍ol'O0y4N:Cdd$9rd'_޼sdBwU'm'&r |Zjr-_쭌-xwekQۇKZzLϟ'mwҬU[J-OY:}9sҥpt|h<*>6Ncmo_4h̒e Ξ;GvǷU{>;qIBd(:)GEEwv^:ؼNÇj9ORL1u7;3-~qؼ?~/Obu~Xh޴ ϟiF_eW8#Fs̛ͅ3'ק7k\o$ !2 {vmZpI5\XG}ϛ='44v0amؔn3[ޭ j -?'k.@ecCX[1!2 _ȧRbU,C{4IݼI? 0jUٽϔ/&윓_hOȝ;7...>NR\lC@ecCPƷ|ԁ'OA1eؤ(I&Lr&CSO>b󆸽gFsuK!!!Iql];w™yC_>rE !2 uZ-9r@0W#4,0fϝ@LYnAh41ܺ}5ȑ?m[m\FÊUk)ACx(A*P;M.ȸOL?wSg̢SO֭_.>댟C\]];z$;~flٲ1wK.gg>h׆1F ߗo_BF̀W/ Eѱ}.V^Kh. t * r;zxJqjU=q {ؿc4-s7Eqo4j!}ߧC3BdFŠd=N-JY!$e!"ŠHRB+"IY!$e!"'q66(/?$#S{#vo$LF%cCV=<򥨾 nyR~C;fA: IDAT":HRd4QO>E_֯d׏DsQ?J< F'D(XܿK6߫Pzc(:;;;WɊ󰵵`x$)g2ZV )Ke3=ZPH_+2+儷!O㷭%)tU-Df IYu JjiH0g#y8gNxhA"zgrbV7fzĔ+姡A;}*î/]"+eNzg${%Vpqt:- D ܼ~ T*Y)wNT"w[$e.V{.ǏܹsTTӧSHCN"E6cAγw9 ~ѾjժsdiDDDp9j׮͂ ؑٵN:},lȕ ghҸ&LzIHaÆ|<{p/^L ҤR MwESNj5F f$4iX\]\?܍#'Nܵu`ێ8+>3gѶM$mŦkO8{pv @ _w[fʔ)iӆyΝ;_TS~ݲwya]2mRx3gDFFR#~PFժ2y4v3XR:|/_fάI_3Ic2#i}x|7΢E(Qkz<1^B+2mR=c:f̦T|ک+*&SBy6oM: ȕ˙aC%_ڵelќ`y`Di֢ UT6(ݳ۵ko8Yx1O___Ν;ŋ؉%|Wle-lWBx'e=/5nԀƍ _ڹS}{%ugW9?_|C~}_d'wu_+V[lyi- <}Avmٲu-K3IrBdv>)G\S.>+,3mrɪeQ}ٶ %g<w V-#ǰ`,Jz- IYd@F^.Q۶`gg++dN,:EJFV'I9Qκ:*(/O2ĽTv,ΞDMw_Ms6 >!27Y)g2jGj˯;~jkᑏW, yzҨY+4QvkIRdlm锅>֖ :MT1o$L(66ȈdOY!$e!"ŠHRB+"IY!$e!"ŠHRB+"IY!HI1QDY|{, MF xRgΞzzio;ۓifٺvT]&-i[N$p1wcfΞ˔iؠ~z"XIʊ勹QM֍dء4idLJJsuSVěi/X @Ŋ?kXdV{{CXLߤżٰq356x2hݎ7֗qx2ԬۀM?mїiZfUjPt9 NDDA+VZ)^ROX=CSc.͒e?PjM}|5f<=T (U{/aڌYT\*|*T\5s|Dk4=o_*W+=KcOǷ>7asn&Wn.FuNɵAr >s_6oʅ34i܈q&ˆzH@@c..S^g/sq ±#سk'O6qh^|m2xɕL_\ >$)/&,Yлgw]ˣvTBCBɗ//fL՗m3'Ýlٲ1j0;`qqɝ;E`҄¤ cپslTVZM95b(Ǐԗoݾ'᎓S&k˔םccinܥC7WW&Oorԟ4q<;v܍1#Ω"}HRNgƾڗ~"?AmS1֗>aJ4O<)Ԙ^^y%>БeS7>˟< 'Ƙ;sqs:q`9Ln>Sˠ~pp6\`:Zi"-KCQ8zcs1\ٶe3)O;&? C\\^[ٳg'""Jr77WSp$;sqMi`MbX?S'1Ǖu~SB9RN# ;Ģ(q+g3~">x@ll,ܼɠ!M䔃;wf&8$NE֭e883ndk˗SHx3Niu|iR?AL6SZhƔi3 &(8/Nus0#oZ7GViI| >pB|5o^]O(\5_=hݮ=/^H>ScTP[<"͛2l }ә6c6 ={vgמ_~h֪ 0^~͗8;a5j'G?q4Fʤ/RAZ4kʈaC,>?c`.F0}ԵoV 0_ی;6ѮK?? 5_9E7 8sj,&FGE2mP&:]b}Bya<tHu_̳RVZL7!c^^3(S,X+$l9sѤE[oS47$W)$eE<\>\($m!2& !"ŠHRB+"IY!$e!"ŠHRB+"IY!$e!"ŠHRB+"IY!$e!"ŠHRB+"IY!$e!"ŠHRB+"IY!;yP'ðHFUv>)g䔚3 !@wA~Xr4jo_j˙ Z#u6d4kՖmw&KQV^K)Y<7b(bx WV׮*KZ0 ?nmu:K.n&xRAc,[NK!WKΝ?p\n߾ø ek׭gƬ9.UG8t(I?\sX<ϞBV^kx Wn\Md\tCy/L>ˢ -a.y&\>1"-_BX @֬Y_~k0fr9;ٙ1F$cMGGGu S4)}{@68|KOSo-tZMu`Ӗ,V!D˰{y0rx@^>~ @&- QK kn/^з {l?~@ܹpqq;i:dؤlJ^ygD;r\_&4h SIv&,\wZ~ٍqY{M˹+$e66ل:vݻw)^8ZmE}ʃ^6/=}O?{1qDW&.͆YKEv\=T (U{lǒe?PjM}|5f<ѱZ-}E*5(Q '"""̍mY@&h;~>ĸ $p1ooDzxT7o y67jn޴(Hx?&&i3fQruV}v #GǗjt ]S'w^ Ks!S&jܿ1cзo4y#W.IF0I_֣w?wų'9w8EaڌYϞ;=;8vOX7Fn2][pjs%%c''[7 )j!>1m< ?rYڶnI9r(׮ϋgiѼ)c[6GőErmƱ 01u,\DpHǎ`ϮmN:_^_젞LX).ͥsqv @TT>Ө(ji3'B {>uƩcGcլۀ5+h< Ev=u̢16v_njWQMPP!޻gr6Ϟ"W.g>ItLYV]֯]M…A3u֬æk4haKS:\<^yo;2՞r<ڷoϖ-[/Q̘12ptt$6^K9{׮ 22Je~PF4lXd ^ԉO7ƎtLy$^+a\cXr=LoLj’ҘۗrΝ;v,ժU[n|y[ٳg'""Ju1qqm/77Wmٌ{4Ύ(} 5...>'wnZʒ92͕~S\aś>˔{ʝ;wf̙.qDGGVVϸ ԙ6c6!!0u,ڶneNu`s& KϲVE`c&L )w38֪E3LIPp0A|9u:[S\Hvmr4 3mZ_`L37#1cڹxG}2)C\b5kUV@O o=c:f̦T|ک+*&SBy6oM: ȕ˙aCo^TX]G24t5xgNʾć|JjU {Av w𡸹RA6h;# x>^'Ncq$4x-RfP~C˗x :9Q>Zj);ś>T/}B&\1Ǐɓ)ftT$ e%V!D /RAu6n/^^;!R$YIrIf:(h4o6(a5$"$eE<\>\He"#,VDBXIBaE$) !,VDBXIBaE$) !,VDBXIBaE$) !,VDBXIBaE$) !,VDBXIBaE$)gpż}({%ożujӰcv:i2`oo+U+WfAɓ!0rFp䏣tqk#>Hy yÏYR0_BL}Xt9u6Ǘ: drt:N[~^æ*[?Gp54dnמK.)kiи9%˔vF,]EQ b8} Ϟ=I;hԴ%>4kՖ3g[G6lB2i֪-۶|yʒ% ;}_]K9fc]vż}xTYժDŽI_sžR\EJ.Ǔ'O?~L(]"aaOl_&X?@(^k͡oJJ~Xh޴ ϟiF_eW${57 [9a߻_ōa ?\sX<ϞBV^k?>J*ϝ?p\n߾ø ek׭gƬ9.UG8t(c@Jy݆M-*)Yzbd\tCy/L> g眴iV܍Vm8;L2VRY4n;yO dIxKl-tZMu`Ӗ2x YfVc k֬ԭS<З퍣#ݺt`::ӨA׊׫lu5\rvf̨F*9)_`o#St зw/{(kGkl߱K_ǮݥSG[zmml,{H2YZTW))z/qOs ͝+W<ח>m|G&- # /Bƍ,?1w<1%Q@y=<޷D| bڌL1^)[rzE/Ë/eaOPX1V3g~oE[TZŋ=%_oZIʖpuu! \]] ^%pwCN?Oމ.sS޷\K,~< 3~UN)>n!fڵW{ʖ% 'OXbj֨NtK>smw}nc,ZNk'l!SپDqOW^FaŪ5|QTݭk'_@x3"""8z8{ Գ)tJt~֜y_/FO?{E[riȑFü&Ӡ~]Ǯݿk<~iɵ#DFFhw)[kVʃCQtl߹ג'C OkNՎ[jࠦo95no_GF. V ϾvHll,k׭FxyO}$)ufܘQ 0}?wSg̢SOϤoccCN1k<za?3KS"hԦfK IDATߧC3Ezڷ֖,HL}~zoBd,VDBXIBaE$) !,VDBXIBaE$) !,VDBXIBaE2g_DGJrB%RЌp1oݺauy)$eM/]L֎jҤe[? { !e2MRV8u(_]ZhntT$SIv&={_/f-[V*WDQZZoVF^ ޺u}ǡ}V/$e6lBP;fA5[oaxbú˛O9u |[Oٱ'Yzz!D /pƎ'Mش~Nbެ߰~24nފ.moԌ%Тu;yS恟=T (U{lUkV.Ep1oٰq356Џ{2F񭄏o%MFIչhZfUjPt9 NDDALǎV/\9Dk4=o_*W+L_D!2I&T(_1'qewI6o+ҶuK͑#GYv^'K)ypwC,Yݳ;׮J;va҄ʤٱkwe󖟙Q(BYDD""&P1(EDLD,"b" eQ(BYDD""&P1(EDLD,"b" eQ(BYDD""&R$BNFԩ(fW+"f)ߛu>}$j6.\@ɒ%R2}Q+"|ίp~ll=;vWED "9}waλڣ 7Sm۶ШaAA4h+/-bwOфv;3uS$%%Hæ-װ)Ovo)5lJæ-IHHvOWҽW?7jFmL.~ zҠqszO>LS!"\eoz]{-k{:/w{ܯtXn&OeŇԩSWlr/W};8a+>3f%,4tgjN' GXXl߱c'+? Сߙ9^7n$N>.Mq\4l2S|7[mOWriw阪ThNj0>vxWh/b<Ν;Orr27mfy!˯nOCt{ @ɒ%7vj*ZVNU92o41%$$r6!sS"Rbr odӮӋǧGٵ#/COش[Nk?ymGXXiz+2| ߗҥK]Qsb /r|7|˗䇝m/xs_煅/ >!jVˇoVrz>rg׿K>[>mĸQXǶzSU>wl%,4)))4j֊f_JJ n۾wyu9]CezKuuykt\}bM>oVureYos,__jլ pאalݼϮ[(S&]gFͮX>v#Y]jתw̽Gѣ{7*U mva3 ߮!00AnX=T|Ey:'vO8t+P=Mp3˗,vc>Bx+Sr|v{߬ڔ{Nw9mNqyz̙7_… X,/ë^Zqq7.=E}2-{YRwNǻFjǟɴͥlY-iyrt5W!ꢗxWY"J(ɴOеKg]un/^C}54oތ*U*gYs"..U_WZc}sO3c+oSf݌4};Cf>ށaq?9vrfYV ٺyWǁ};00g_X\L=CٲYW'*ht &f(>5ӯ1b0xŌ56fT\9?~sg?s_Կ9y O?w3R(TBl6m6cbyffVvFN|s7#ռٳ9{sahmZhN>psWʔ ,1iݲw3n$w̚ШY+n睴o61F2`홎P<]{ٵ'Ùx#7̪=4}=SnT05w=wѤ9s #陳=2wdXbؙӼY'NxҥKqS.;mZ|OʍnFEꍾW_^ȓs^qԔ ̜<^,\>r ׭-H.>,h)'x },5c=iivf̞K+2\tzG =5U@VZUtEJj*ݺvvJS eetN7gP6!&7DDLD,"b" eQ(BYDD""&P1(EDL|O_9i4-)}a@-@BXH!t:8p>!pdjP2 "80E eB̞BC)[#+a>oz?=+yߩQ(bAԉhJ$v\W|K,R0b2xրdQȵcW<'a˺/H>zP|eϹ3ڲγ3;'^?S(5/""y mܒP'#\F)rXRߙҠi֨MZZ Usc~g,V)bosC9޷0w8 ^ַ׬t?r>}NԺ/8|p_5ĸhڪUfOu/X:M[wp.oNJL(\.,]ΓSg\nHc z r#$e.lߙ8g#}F¸4{jGZ.ڟPްqKbؐ)M3jsiYƿ3ngyb45kEfAc:DvefԿ'I E4a؈ǓHIL|Q|BB"v 1Nyeװ)O$999cZut :Dv;`8WN}i1Zu{mv _%},]Nݩј}]|'t]o䪾ܜC9]WChjP`[(PjPcGLCeȄ2t N+3:;e7_/9~ ^xѽI1ᡱ>X>?{11bPvmb\_63g%,4z`ڰ| ߗҥK_z=69xnV,cmѝSy޽?3?޽?rWiS|rT(_iONaO왳TRg׭r5۵fQdI4͛9|/]K`=XGLr *Sxq}a֬v?4+ʘQ#eyS_:1ʕ-S={NXfxʕ+ˆHNNf3-_Z}x[Opc8wC{wwE}K.'F4bNuԨ^Fjǟq~uKʫ,|y%Kd'ڥ3vfμo?.\pߗvZԹz ᫯мy3T @l)쓩-9AXXiwxfǗ*V~=rz,e\dS9o}Oր@\NNlN|pRi[OT?(lǟi7TZս<:&AǏ#88@RRRPϞMZvr刎up4:GM7alظGŽ-x<:RD iԬ{ÇEۇ]̧UPO>\Ax OS{as[z:S%9.H:@6;hsO_|s4i)WFÆZ卷!%%SLTK*δ<3sqY wy3vD4ap?TBl6m6cb+UkK]3 'YwrUC2t]Yrd)sf`ohԬNڷԘQ#0L8 ٵ']{R)0&͙|_{ݼٳ9{sah>gf;f4[РqsMD]s9Y8s[_v ܜC9WQʕ$,,?D}QV^w(x~ӱf`>ұ}[c[H;֮\Πx*4MDI>έhݩ)oݹqJ)%r/~q]_sD 1Gmd,GZQ뿤?>K%+#ea kAӶP"[9/`v 4\)HP\)"=h@vS(H>&C +M7 e_|dP),o6 }'V4_',ɘ#&݌VwzHY PQs~ۻV2ZJH62/H!DN=P:SyHaP)4IzӬМ(EDLD,"b" eQ(BYDD""&P1(EDLD,"b" eQ(BYDD""&P1(EDLD,"b" eQ(BYDD""&P1(EDLD,"b" eQ(BYDD""&P1(EDLD,"b" eQ(BYDD""&P1(EDLD,"b" eQ(BYD>F IDATD""&P1(EDLD,"b" eQ(BYDD""&P1(EDLD,"b" eQ(BYDD""&P1(EDLD," /jlI\.,VDT ,XWwlx5 +W2G?@*կf""bcG)^媖}UC~kGhYD4Jh檖{UCJWH7DDLD,"b" eQ(BYDD""&P1(EDLD,"b"Wc"b>ˉhr,ր|N,R. Ğ8^)V++T]N-*ߪ"E`Q&1@){j ;nf("t'8X֧Hf.NawcXq:ݤB' mnb+[ e\.ȹ3Dat+ kу e?`5+?Ht\NбmXryᄴm!7" /'+ "PNII!u"*9Bd`bbX,rN;b8RFjr)1R;ٕMo7Ȋ 0r0RN:ұp  V+`⻇`\ 4 ŀvip?R,vS,44_P(}))r`qBa](;v93=R '^;?HxٗˁXZߍndz .\,±:/}?c*XK\Ng^z 3ݐH)pOEl{ΟNưB[ӧ*܏4h3'1ֽ|yL{;r~\# e?>}NIM`KA:Xj4OǞkލOdYסg}ШYKۀ/3́}\+ e`qi$cDa4K!PTk]M&őrVuq0wt=]{vSl0ɟkE,z:.]E Iqm]RX{=SPZz`|R6_Y /} ,4A4mҘW_y)ˑrJj*=1͉hܜ'O%%5ս~ tهFоc$˖^p8|jˍ$%%i,"%N;FRƉ}Gvq;rk"{痳:үooؓk}Ǐ`Oz{v%GW? ]ۣl̙E`h|WuW\'\ןŜلʄy^zrjʖ|rL:Vl6N:͙3T\f׭#?9+VG'=k֚rKDfi[gZh8yE@!o^~}㋣e7q-hٙvEaa9}*؞KSRuJ?~h^y %K0Gy3ֳOr-}r-)E@NF{mg2K/Z L5, z`2coצ5_|5CS{ʕ-KtL ըʖ)^ߨa"aq<1u:QBr|=+VX /DEO-u;wkك_j.e[?ng{;,#秽?)9徽{2ksק{Iq拉a\8N;N{GHspA&Lz̔s)/] i (7V"}_`Yyy=+1~K>`+u8i> \p)gt],~u?"RRWFF3,ӶO̚K>ݣ;^߭K$hj^w͙^7zp /N:M͚qFrP|,0.`}>~i`|ȕ|z]_ϲY_VM^^\muN`[03gem^=ݫGb0fpƌz3Q()[B0ŒA0E+[BÀ+7n5 eyQ 'XBKZKhi '߿g0(ZP(K)b!s߭3W^b+H,z-;AAAD}jj렎ZkۤAA e)EAFBԡ{j6fc9g tRTVb>R(mATR2aav\}cj%88Ŋa 9R(|jBhWS BYO\BY\;I)E2Kt3M/ZI*E0JU8ԬU[KJX>0"EX@@ Ė7]8sy%? ZP2-[)E0J2eң7{*˥rXl"E-bnr/"b" eQ(BYDD""&P1(EDLD,"b" eQ(BYDD""&P1(EDLD,"b"V{_ZM -""~)>c'>Ʀ#- $"RS2nn \H>_ 78C?6Z ۳ݺwd}z Al!N0vc@%۷5QDD֦WdTŔ˧IENDB`help/C/figures/preferences_security.png000066400000000000000000000456571304552132100206070ustar00rootroot00000000000000PNG  IHDRe 0sBIT|dtEXtSoftwaregnome-screenshot> IDATxw|W.vFd ˍnA2T(ˁ q,AD@B)+64i~[O]7K:ŵ}Zڹ3 ~QkX1PLgND a[i$&e\~bDBˇI:|OґWDTs }ۘNYsK/œt}x⋷bCöS2m}C.H?kGT&%1N,"sр+i{N"wns{)Xnmٯu)H=W)lˢEgqۢq_,< x<S^=m࡫3sm>Ya9ƺ=,]q~FgDnxy-~΁ XVYrpp0[4#z֫$ t9LUܬe.0sss{P:>]Oz ΚgTx5T:iG8[INͻkݨXEعWhlaSz#Aj߽@εޚsSj}gٻĬ25TۆZLpptzˑ.S-y)3y9yl9L9ƈz8Á4kޒѣG9}+-uڴoY%Ϲd:+0{;@kw1w7)wRh^%?0]=n˿{Qn'8nt|ppʹ11ám<7ÃJ@_?ƭ#ư-] FN&T*/<<\B-ٸt DʻKeyEm¹g@#| c{4yrܵ2~L9 $_}11*aضM@@ {H|A")+%VAio ?.]o22Qέ_T7o5'{ ۓJԊpXw5x!`c Lo"Δ8K?s^6.O`h{QҎ8<%s_`W\**h}| .KZZ*#w <J2Nכ/&217 c% g'\ҀVs\ ?gI)n>Jh&}~;S9OXx<,į^G0ks)2/+ؖE},ӦH dÇhڠ4sy1#1*~>Ӣ߹eNH9sY6p1iQ;T_V/%88ʕ+sojB:dսQ7@Fi[vQtZ<ڱՋgߡ$u,[ݽr'k"Ҏ"7d.<'@ٻ3C;[e=O>4lUc*A3ڬmFOU?CO^c]YeʑB;p9"!0߸3ydZP{7u%O);RHNt9QEʁk;XW!ό]q$Z rвv8hZ\w_/=>FNMjr}L Nή^;_)]vu8zgʹyaacBYʇ!ᢊ2j;ILLdر)TDz HbJyҼթ $26T qpv}s_nnc] 7 "26)`[18 P7вJh*8hY+KyhsikXlпeג}L9Sl.=kAWO"+C @ e9@%;.Je01_Wbo70mlnɻ^֮D6]Yڈ엍}/_CA^fU,+N"4pynuTmw|q"Rе:/NwsQ""gQ#T|._IIUyWw""T 3Vp8f_q""rrA""Q(D,"bA""Q(D,"bA""Q(D,"b.@Lr >}])tۂ!?,z&+PNIJ+;(-Ia)r ĚMs:U~8e[l?>Bۛfw9*Pgjji@trfY"=;2ۍk6β<,Uh31LIV d?UAai(G8_ZD:l^ʸ1fk*<8Y+.RNHCٲΟe,2.LĚJ4g Ǽ]O튻}&?L-/&lbM%3P]d , v Bܳ(kˋ5XS >s)љ jҼ n\WĚJ,g ec[$_kb݅Hg~}(|j˜眖ؼ"3mS]q ƶ|;Svwݻѹsgn~3=w/RQ6vGD emP۶s,lڊrɭsZu)u㘹=}3?-iX\`[ٖwAZj !j.W is-%qpu¯S'vk;"Hludb?Y^m׆O>[k9Ъe *T(_,OF͜yx_>-kI_pb`ٶϲl x~jשr*\z,?'执E붜հ1wMѣT&?[I޶fu5j7ߢ]ԬSϧFԨ]׻AW/|z)wZΔZm t B 0ϽmnϠ{؝GZwfs~KhަcjWmۤCҮS}!;gLhֺw]}]{ѱk/&?ϙ|al<TfϙÆԕqҁl_y .XcTX믽J,]§Y3_@N=xr ;wE3j]rm:t㈉{ 2fO{ZۅAWzxf4Gy \x lҾ{ϽYIrc{xٰa֮&88'| /ݢ}{|<3:`ݺ,Kwli߽swnK0n(^6 ˓ci:&*/)eԙNfjO?uڰ;ϦM̟>K~ZDppO{g6ld_]m+ws?`>c2{J^׮8U8_竅ٳw/xknzNcԪw?Ei٢1q֥۶طO>=Gm{>f͝u\m[\s`fϙcOeƫo=z5ӷ,ׯ˳S=j>|MV,>{23^{[`,S_z)aX~5jժNYtܝyÇ}>3}ʖ-ˢo{,^BE&?<ۖ~?.Y<~L{%n7S_;Fs;|Wc$%9 tzL}1ޜ9Wxsτ1׽+dk};Uնv/yzxvUs̓qqqDT3>6.P5mbJ:ۖ+WQb6ik-۹m[Sq7Ȥx X/~\k˾ ;ts]?o>ὝXFT #&6g.7ld-$%'hǺ cʕ pގF<^Y./ ţEvC3}ر 1_@@IIIp!m]}z3e <<~_E՝ իu67:gH"Wi޴ /Ny۶Yr<,?|)U˜+T Y;v?cªTaϞ=ԩ] ݻfT3>:z7U*[{|Dvm5{.7>g[jתwDHH0=v͙˯KSe=fi|ͷ|Ԭ]{^:Q '((IXٳ*}מkHuDre9v,}/{OfLuaaaDG^:9 SL\7CST\oӶMko!7\}QVM"nc+1i <y)>Eٲe MoɈop|<>dFy^Su+V$rk$g7h>f7gy\Й卾㯿!%%oȣONA&>[GmYx,.㱧+z7n-ۘc~_N aꋯwظ86gL֟+/>= 6..} /sAޅٶmzt1|;:LNqҩϿ*_۶"]:u(PMΡFDu5kDЬIcGcw)gpQ>ko?RRR "00{yyWue^fIH`׻8)g]ƍ_~M~\1Kbӽ}q:uCn{;~;n_F}Y'g?мU;]y ]t>}[t 5htrV xEޏm_E#m֮k,ϾŃ\r *=mjn>$#n_bUC׻n^qMC'ч˵OA[y͙ }{KѥsX]ɰ?:FOw_ĚOƙ+6t 7vZxN~ǥsZ3ɵĚOƙ3pH푛?/Δ gb'SӪ%˯rIJQ(P65XSJ?S>>ʆ3fk*ij3qO)ĚMI(PkE=e1X5dǎw w t: E)y<8)*NҒq1ٵ_66 gLDNLiɇZڲ)'ȿBDLtv`߬T(D,"bA""Q(D,"bA""Q(D,"bA""Q(D,"biԨiK5mYeNɗl˦ |ۧryIIIg͢lAZZaaa4iܐowևygxx ^C9k gHN 6ȭLz`"\|AA[3ߛ׶mf4apa^)ї/2/|G׻Y[LVU ehӞ᷍e˽}g63ߛEӬU;ݏ7z;_x~jE]gQ}ziƫi3u^MŊ s ˖`(>}g۶,_ҧPδ%2oZ]℻cݺL,k~_3e9߲igN}vj9Wp6O=-7cْ᧟ /^o?J7{"s]ASDS*Byq-[g $""I仭7 쏽}{C&{7CC{.оVoݻ/ҸQ#:vhOhܸ?+{-~H(\94Ls W^Ctյ,["qШiK={}]gV  66oò,9yu'"R7gʡC9t0Ny@3&3ŗ3oG%%%aĨ+ IDAT|7:|n]ðeuBܡC|ĝ|ɏ=FDFμLy~k׭pжM1^wo :>1]z$((vmpaޱÆހfևֳ7ujbmb ,;Sv TM`׵tEWeWG3ѣkg{ɲ)k_87h"(MDlGowktP:/_T e(ED P1BYD e(ED P1BYD ׬m˃"<9prp8]]M%>m"!>e~e=]tR5]Gh0|VCqeԭSބ+B)ɬYիӫw?B?4* f{D3p8x.ID rЮS7־Tr,\.cY`yJ~(gmrEiLLRvO_? Mrrrmt8Z5΢Z]ai۵I~]{m8$zx8z<Rʶm8S^ыʡ92tjZ*?SF7ltJmrYb? z`.=LPNLų#'Ԅ2\2s{҈܆mpATǁ={xJ̿))\S)>bZ=r߇37 =J?1nݺ!!ſ)1>gY+Np{,֭Czuӗ[קaóP nǓqǒcO;֭EI8'@Bz{pϸ?gU<&JM([4ܩ)2DŎ;ҿ,cxSS|` 5uyi3IIzw8޾ݲq]\.|UB+\Yտ0ۼ?cT^y-43i=j)Y߇+#p{73+s +z7SӒROBhKcԄrr3F.W 5r Is9&j;wEsVz9Ny=Q\9Ѯsw\x^5y͢zj]Ϻ%}mo٢9Ϙm,Y'=_ȱj+>Kɔ`(ov&մ )GFIؖE3 (1RsMܮOY8 bǎ]ҧ| k]W ǟzO>;ۖBppA슎f#g̙3]wUL;v"-Ϳma}>_ غm{fyz0vܺ4,cy|7nѶ@8h)9tS!a'س%n+Hݵ}pl%-vhRvL9Ng1M):Se8EFp8~p並le|Tζ}=g=6<"_mیqwO>O?[Pܥ},ZF ]I -6s%"%|eKѝkoYe NdV&=0K.@֭*BZ$_Ulќ@Z4o+/Mc63ߛEӬU;ݏ7z >yvtՇ?:|GfNhٽ4Uٴwkb йSG|meY[{M[g[XYrwfm~~^JӖm+}ScvQHib|(W +s;8Sh߾ZΕ2>g|~{?yGjɷ_-*sp%~;r: v8&GٲrvAXª Fa̙qc([, STDz zDJ)wҙ~޽eo0Egӹr@g9s޽ޟ@5 ]S匳L~nV c}Qz<8p*U;,,,}9O`` @P9DzK40L9S\wUCETx_2oٴ7g=ynxg y)sӿo_VM8 UTDz N~ƇCoEKjj["#ޭMÆ0i9@bb"K~[Fx FxYbbc9|8mS6? III4XfܘQ4iܘi/̜y 8z()))c #Fbf7Ty}X߱P%ӹ_"%/ʕ+ǔ8rh(^{6t!|8cIPP0ڶaü}p=!!!#zGʕ7f'ccO0v3o'DA+`lf7gO=&R%6=oܘض_|fQZ8Ǎam0XQ1^w? w:Kp^lcza#9J1[,[]~mm'BDLP1BYD e(ED P1BYD e(ED bYmyxQjB9# 9Uΐw et,R)RʶmaHiuLfMypض5W_u+:&!kEu\Acۙkƌn:̙=5kpp<+V䥗gЭk|J@WKq1wyv }LV- ~+66\ue1TYxErn/Q|_~ӰIss r'{ܵsw}rJ sc{вe lۦez̙7iJO(wd|2αlXyfu޶W^}W ?cͪ3S `:zm׎ğ%99X۶푛9ϾZ͢o_L}i۷\O?|˞={y:Fڲs~5d{]KZld7k泾yf|2A4&3>~s!ڂ9x bY#g|6w&? իQlY.m?ԪUDv#˸#,«1YTVwl/G])8c{h AAAB+E/rntWCn_uyG=ȭ[IKKòl00dQFl 3|(<zM)C֛oLHy;_.^5믽;o^7v~z<޶[wa-#8x cͭOrFq?ǟ"11U޵ Ӧ>w[ۦ cǸ_ƍ;f<Գd te_ YK>`\p5<-[D_1>N=v,[}sxH* &-%ٳaq;zm$jں=Qe-ڇ{vOC0 41kvҵ%9򟿥מGGq Lٶ/OHJ*A_Fm[NPD1*n:KԄ2'?RHJ3[zB?."tJ|(\tԍ~eú?X%3"rjN#jо}GCw99Pv8T\/mYʲ8\.C .rr( p)KpH.EDPJϷĉ e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BO )IDATYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(EDNmQi(;NòVD( d.NgўdajsDԬSӊž; ^H.Pnں=-t,"rj4o۩H-P)CrJ31BYD e(ED P1BYD e(ED P1BYD Ek"bx}4'˅:S(beϲe.Dr:T FZ% GUgv eRqeԭSބ+@O̚KYj9z#()ݞ!R̶-f7p8]RrЮS7־T e R Wsuʷ "϶m(NT(l>-"P9!ϰ~ݿ8OH %#94P() ɤ,An2w Wq8XXnBZ ;Ra$?9;(G8ptreOdBY `ф*+z53Np:i؞v'۝#-$>!eʜ},-"g.o%$r@mW(oy|X8\stg:șΔ-OǾ,76x&CzPQ:AϦs: Mԧ??]Q[Ox\I/$9ؤ_S.IKp023 JT]Ҡp/~Y3'uآ^N EAAׅIIMHKQ5ZRAF8K(ٵk_۷u[[rѴl۞47|S. e3}BؙgT]ky(i [9һb2/ %{pT9aw[ @Ih;"a(ZH:ZEK[D rPb TӉEf:p)Z)[$ &KbE6aL> 3{Mó/Hhvʱj|p?w9{vQS}e%iɟPqkuNQ4swc{_d-) sre:4R.Qc׮' %%n bvM-Yʨ1c5f,?}șWb|uSt- Rr~n9|:e)]Z.:spof\}IÌm~|9f53uJ^3{v/;3^&`b-`ݶu$N¡ @K\n׺P~!ƫm[eፄO-SR9>s?mN^>z~j*^už/YLoP_@ΤvkF?Pqt{׺ȞqKn)+0'΁ԡbdڽ:߸}}HNN⦡Cy} 8)E!bhz:))w7 >˱3R(8@gGixxOS"a3ґBYΤ"G\|OIuivP,ҋ!}}YE'N KfvBYKHpB]#qi.6ybbv=h@gGݛM gKr$>EDpa@4BiO""up ԔH;$IENDB`help/C/figures/preferences_user.png000066400000000000000000000371111304552132100177000ustar00rootroot00000000000000PNG  IHDRe 0sBIT|dtEXtSoftwaregnome-screenshot> IDATxw|б wE*\DE HQD((X+"H^.BYXR 9I}uwΜs;n6Щe ;""?_qᶀ<ܮ}:bb[)x'l+3vX~\ gN-kc{!`3'9s"'8qsߌۻj@~c`xct7`{sFԗvm،m]elKa[ICyÞl؝||ھZjPlT}8y&1>etZ6;yDD@IXmۄV]v*$}5JhH@IrCJ /JAO{:k.o@b]z 䳥ǸAK[RXV+ṯ60c5n\~MdXn˲}rc])'? m7ΓVc+b,[ObYɫ`UOTV@GItõ7KDR6 xi=pB{FT+8lؑu$p$IphL,HzOc:z wO^ʼ;T$#{=N^x[)QnBW}}5ߝm|BjSyoBK\ %f4N_" ,J>q} }Ien+eg}yN>FD :)YP/:צfnW}/[}K|JL\,#w#˱p IO^Iñ͑ ܳUz}pco.5^{\}M<ͮûHa f}8S wĿq3 m~)%SY3m;I5c=à Cퟝ2oZ_8҉HlssK,oM? mkyq8pW \1kTr+RjU5{N>'4znr9ۖLHp'.;cwrQn?Ql؀˙8;-Ya}R`z_یsnqd,] (U4aG3뺄yV;f3~LILE 5ʄ*%Or .:#c*fp:ϭj/tud[Wus)Z,~udX}?iy(rMkO3dCB p((JՊgnY]vQ+>E("@'[жza_K~e@ʁl $.c%!.vnߴg̛V )m b{l %ppr{ViP.9 qP#Ohh&2,_ hS#k>㦷RNo pSihc~?0 (`s&0qI;OEdXUYۯL[F?2oZ}ږ6e|oe宿Chٖq5XSnߎe~e`̵9fk6NTR7]f`@\ĚM)1~_)ۖmy:_i|]lٰeL)1~el=Jivɨߵ~n2Nu*\r(cz%ws(ۮ@4dz+JRR^nZ2ΎiJ*5e/;R߹s]9 Jɓ'ҭ;~O]CzcOx zqT lc/_zuk7˯fPFu *:<=7_~5 bx+ 48'du̜1UVy&M_Ŝc3}Yf5s~vJj|qc[ZUk5nK?ٱ4lڜM3z$$$ϹͭT0? }9g{bb"=ŧ.۶qݼdnhކ: o'q*.۶iNm߶}m;ܙc.\;u$< -iZ1nnKF>jLcQ{Fömvˣ ~zzyñ6kZr;55ᮎٴy=))^itC >,כ1]OOR8N 6?mR B9x GR*^mW<3DEEV>ɏ?gԈ+<Π4cg}Ҕ9r(sɌit >)ׯ^ KYzos;ٳ1-ת%Wr%ʖ.ܟl9Ԩ^ʤ?dMLc#AضMUضŁC=F"7#=O>o7Ԑ,^P{ ۲q#V ۲nO-?WiFYiϞlذm[c[6ڴbÆMݳϧ_ʱ(^dsܞ-MD ۇ%Kgxg'(Q8!tҙ͛xۿGoM \of ㉎bԳִnw_zNdyKk WF{ƍ ǟϟ;wpRcH>e%::[RJe7mTRkլᅢm2++,Q}3H1Ο+\Rm^TR=Kd>8rQ%=rgҬ+1ԬXt) a)\*]f-1qݝ|p8WϿ8wRgF }ڲf:F {".6u =J&4 cU2ñ>ceԞX^6[lL||vH}ҧ@Xq DGOeV.HޏЯ#ضͮ{䋯 }M*Uذq##F=C.{ <֗aY<֥} }jώIҥؾo&Oyo'ξ]/ -}+aȃ/hP6|>ޟzGDS(YxАʔ.yLx-SLYbbϜJ(}kGy%uؿ_"+F3>\߸ qnhuk:;}޽(_l]271*+˓qc۲|L2rOHp0ǎH]Gf^.h&LyLz]֭C3?%^VzwCNuQ.O WGi݊^Qzm^<ֻW4}]rZ06[\E;y/Һ%_‘#G=r^ 䟝|iu3/:#G2[dyڛ]ߘ\=FFnc̸ٻͶ1&oҾmlצ%&NF_JL^*:UyR#GdڵeԞHHH$((OLku)}v[6$bɓL|mrсPZ,ϑQ}?3}z>_](VEKrkt~gΜ^| nOZhΣ=Nzlw@xfK<;rhl{K'`?Ρñ/Wݺxۛ4ϛ~@ͱiӪ9MO5s}ztKs{?PfuN>M7~gM~{vǶC]\oF]6ҫĝ#XMgQY#F>8Ͼ0>%ӭ}Y~iNreС}LmIYEK8v%6WHN4m}oNqQV-MTVle&~kБm݂nNwUKmϯbGA^)CrN&֜ݚ2gsRrs,Yb}\|r? }3֔Y?땒˟{ɸgt8rI~e#!fe9]E3fk17˜.9N#JPncCB6C`:~^;%Hrp {vla?37/LF!"WET2ܶɯ;ED _Q,"bA""Q(D,"bA""Q(D,"bA""Q(D,"bA鸶J R#|?tnjee9sB R*:oqQMeiHN0> 7hn[{UHHN0ŲeQATV)o泏m|'lӞ5rcּ_Ho{Fgoo@uhwl Z>sjK՚uiwM5PN׮=zǢŗzD$0>fL5>b܋_./枻d+Gm>No{FVXɬ_ؾ}F}"UƇ_J8z3ňQм-̘wϿHsIHH gO{=GXX-n ={z>3=5bERhQ=5$qqqAz*"ܴIcfc3/BRR_>w #ʹU[J 5j {=% ^"9RmKJ5Ơp8yϽԬӀӉEV}QwEjS޶H,{bۦزa1%n۷څ1c Пo!O]:E\Ƈ]?q86$m 7.LxUN8I\\ X=zy)T{!su;vǎ @6oJʕiܨ!!<~ƿ%@L*vS4<|»σ]gOI n<}=ڛ)oMs/=;v˶Ohz͔-S_ryxLxUVYN<=l+"yYEK8v%6o֗@N?vM\Us~.?;qſD;:]X1{wң /_6 b$%%w>{D$70Ens[[9YlFpp0ժVIӺU.MDrm݊[t i _IDAT"K򅈈A""Q(D,"bA""[;x<ON"rY9t 夢P<7/sٷgY4EpgG qs$ e!1!Bū]p誖]a[֋3NtI>ʂq{ߴ?.[!K١q[W\q G+d7\9]J*W("bAʒmKDAD(WJDDL'BYD$W?h6*U [Wӽm'BFSzm8qqqV~Lf͹R4ǯxm:R՚zlܴ۾{zjQZ-|cc}ŗxskV6oϕ1Zns֭Y;e(]4o&jka$&&uWqQfMjéS|~_>eʔnOiԨ˗M6lذ![V*?1Aך?XfͷeÆ̞5̸&_vgӝc|=m*kW.m 1H><V-_̊ x /fvrt{'?usk[xj1RXt)gҥ:t &K. tI&n:̙ڵk j*̙޽{/^̬Yشiڵ's U(s!9JRь9o۴3=r8QQ(P' fܟ}6b2c3,Ihh(=?̆m~M& PB< .\EHHw{8{v϶ϭR?6]cƌx/^1c7G<|'mֻ}ԩ;h ,Ȱa}>3DDDl7nQQQѻwo֯_Er%K̴"!!!UW3ʼnlظ3gΤ9GѢ>vYR-[C|}]:vHڵ}8po1ú"##S~7oJhZ5k;ob6/!O gŒ?(Q8LFdd]f,X8jipIc^C ػw/ŋi믹{(R}n/Y$}QQQ.*O\r_'>=5k֒HBb"Wa>~>;HJrc6yQN<5|$vveV_LHH $8`˰/y̬})o |g%66ѣGswGGG3sLNʤIۻvʐ!Cصkn͛7-O{NhX(O Ν;Pﻗ]׶uKzϮ{Xw8x̘1;x8p cʔ)tؑp50`%W95ml/Xc\`f}ɝ~(Mxs+}|ʔn/]4D.zK.)Nc?N\Oޝ;(o\+""Q(KtBS(D,8ײW%xgRRQ( W+^xtynxIXAEp`.&;jVbYzk]Ng KWV؎@OeBYLeg.Ar5xٶٗ/~iN Yк-9]BP/۶-sW)Z$w槙T eCT l۔E6^?y} e9Gymy"3Ze%l,cymy"O,t-PBQ嫏}i-6Ë_~\6 |8o{k?I\Uj'%%1kؔZ?%&&QԨӀu0l('}ӫU}l lܛ9})_'2h@Z'_}k֬lذٳfr"Dk׮cucm~޶Ɓ~'~#˯8Y7Zö-o{ϧ|!:xGRT4_x6m FNTT$ !;g# %Xtǟ1z")\#?m4#"(Q8Fgw'+U$s%Oy⚲r|{<swޚk&S`!Fx-n & ڶp8|,:treˤKe˕-CllY-ayq'%}\te_(nFʕ}o޲RǵjwĶm~_CΊ%PDq>]ٽg/WU-""={R|yvKtV_}':/O\r_'>=5k֒HBb"Wa>~>;HJrc6sn;ڽ͖[ylUw>7b8q$=?vۭ3vcc9˳=ڧ9{K~oz#ϕ|>剕r عs'*T}ҵڶnIٵ{+V_8x+Vߣ/2ʼnlۦGmCĨgy˶<0q.f_J~UܓUrGhִ`>ޙ7K|Cir}ʍLܩ.']jOKkt)Z@Z_W⯾+cĝ8_.;wPX)D:K,^|!(eew$<2BY1O&GS(mjU$ L?LPsl d\6 e9Ƕ7Ջ e8<|w\β<882 S( VP8K_e flSLe<sTBRjԩǺKsXVRN$r8.[ZuTqke`(ULC%yO`!TQ8:WBY *X:bL[!)"W(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED P1BYD e(ED~ e,˟ÊŶ6EfD*aEDpDD~ *a2vnۤղiNKRN#P nRD$_ }""Q(D,"bA""Q(D,"bA""Q(įf-"-[G=p8\.NeN,ٖYwbݟJN%JҤ鍄꼐BY$x,[re+pwnOb%&ijrBV,_L +$ٶſr=q8x.)qzVOYJbbBYD. e R g[+es]D$6mtޕ8 e|+)r"E=;[#=T[{rӝ\dyBY$b(Ǔ0k2%q+\8Nl, ,7x!);'NrtdhԎaB,R>u$EbVa] NWN $lـv''۝|ߑ@X*H$?I')RIJO{W - uW`_'[5ss_XGvv(EYt0 q I8Qfr[i(y-ru$a}(?Yo]6iε}zK:zǝGkYޮH`_씕;{j?yGs4 e@Z8on};{gWi?бSgn)͟(noJQ(T3\SEc͛‘8ogh96tO=ǫA=|'ËHZ5ykipլKuyj;ڶѴYs~v3mИJUjﱁ:uJ+e)ٸ~I>uߍ;WXu;7Ù|;G4O=Kpk,_S8Ub9O//}O2x@֯]zo۔7f |?V-_Lpp0O5eR>ux'ֿnlϟ5ǎQ4<Ιx*_W=oAei6ڵiz[hN{*/τqcm=|CTPG{u7rPl{mSsN:PB[{Ɛ}f QeL#0PNc+0 ְAA3cFo[6i?ݻѳ{4MP.#4D qSy(Fj#4IuMJֱiݟY7Q(s]"<#KsoZ" u"@!WSQWVɟ֙INH0&R(iЖ@TiKٔv_ҟ,GLD*H,\J9,4ڳBM?֔.)L2e S(gBY$_5@ʔ*EpIPP ̷׈/BY$s:ZdN,O\IM,\O:S(e';$m.CHp8.?Bū(Ug6x"y@F׳dfl"?s9~^"yHѢhwbe律.A!_"y"8$.E@r/"bA""Q(D,"bA""Q(D,"bA""Q(D,"bA""Q(D,"b'@b霮CDD::ڎs|)6f:/II9YH 0c;#718q,d/ ׳Zpɉ:ED2\)5ml_DD?/M2IENDB`help/C/figures/preferences_view.png000066400000000000000000000701461304552132100177010ustar00rootroot00000000000000PNG  IHDRe 0sBIT|dtEXtSoftwaregnome-screenshot> IDATxy|W#=Zؘ2YJ9~1Lgfne cfP1s sVOT$?Ѵi)iGy|& PMkhBY"6}aж8;ڣP= ! BI&.> @dguYwK@5-q):x@SتPکP:s65BjB|x;H:>X!7{I~-*h z1UQڽԨ5Z$! !NUE㞾te67<)1wqW8I m_~ԎhEYc!ϴ R ,J-UANMj)o$a@u͓ \AVB6Jj "6* )YҢ_~]aw@E{HBjIQILJh\Z `g)?OBѐh1FFIM +b^l"=D~G{k5j'>@̎tңV _(cBV!9pR>[{n\Vl[T-[efjF$Q5Mw }"lmrs1s:qWH ]ul!rCH(?򿆥t$To+Nڦh4Z<9RN}FgtnF=u8z(hRU*U_B芥D<|@ESdOOM*R޴6G-u؃^gw/Y~oQE_G#DR  i}VčfކT1ӵ#r4~ebʥz(YQQ& JX7$F7Mcr"99WoSU6$8*)^H.X3͓ܘՅj[;?Զmjϲ] #G)<|lq1Œr'9DVj+?# Z{ .½-$%6& (v,Qӡ T.nK5`^ƹ70/D!VP8YéIsmjuJnp]E#5EmRʷw7h?;p1(Vj%hxeG j㒠!1Y/)+߰'<_ C4'yK|jhZC0<;a`?@ JL1 ׵Q;)!!ТQz ʹfO0mnMOe7m )O(Y (^'[xYs 9߯#KԬۀA>fש\:@7̓VEߌ7{od x܍N/+!uR] @LcӰ#~.Rpuu%|߯h˿ʫM!{KI4X Ql!^PQTȼ9ؤ9Ak^Tۯ4~0Ɇm~3r[Z j>9;b)«mtcN?5ϥ]coJ£[gHHLdxbs,77۾ %o`1 liaFwnVr3%s͈$kݪwQƽmqDЪyk8~#^P'} wȽxzw̲}N_C#k9v=wa;I~^`V?wOLCia Be(Pp. 7oM m4ZD`5we{nd6c^RV*h9']W9Lb\")7xxv<6JqDt`B]nMY=)HB;%PՎw~iEMEۙ5|3!QI'ipWP#x7c~I"qꅜB6ZEk*R6=՟ Ej'Ip6w$_c>t# )\R*o"..Cܼy /jO!leg+ZVyƞҬ) -+q5Ҏ$-jMbOjoNkmmBWiǣDhS$*ܜ~ꕵɖ'@a)[1K--NjtEbf&ͻ>-96T>xgO|ܢG^icH5?U~X<߅39R^>r3Vfi]zƓ'u-=h5ZӕukfVzu}3Slv-VTKZ[/!hPӢc(2GXW'aa899SN-rOYOI[VMYfHlu[vˣ8]yqO4kٖ55cj$&&2vdj7ů||^*5Rn[ZQ?3it36vPʕ.Ɏ]{nתٲ}{rRie/s/{; *{] Z5q)Ъ{ѣU|$͚׋VB_a)»]ť(WQofqdZ c妞[! u}Y1 #"2Zf݆Mxݸx_]yrr2, 4g|*YR l~evƗ~?5z{׏_]/ޝw xfȚU)7!q>~+>wNWw˹p"ȁ};Q왷s8;φ5?rTYc06.V/"*:];6qJ;&?#s|ђgǶ lߺ;w/3J{ZtԾ ם_._Ð|޴cUg'G{jo\jlܶ ʞA|4?nJ{kzV;o֮ϴ'NfUڱH,]fvȰ;7^* 9m?ɪ&7eҔ%_.cc3VL23g&!!`;S}׃2ۛ_}ewpvvԏȚrig,S?hcteQ'G̞}4 ע.fZ 3ʷQ#S7WWF'i;~3ԏeL>ukT(8t4Gqu)J׼ &{k׍U})Y՗M "2VK9qZ-΂icXhZN=ObiN_dkHH(_M@-[֛kZsqeԈlu妞-VSnqvr!>fti3nxRѻ{7.]WW헝1r.'}hxM ̢'O/o0<0c`l6ɉFr-:»m5_KIN̴MTjRk3w~K}CB4& OukP(su)lcc;},EFE{\=SDEEU]}dTycytoҎ2߭Ƨ^Ma Cvt~ZWqLN*ʖ.{qtPܷ E2%qtR7 .x׬HTt4u|h :P8Zzw#"2R/rSsY+ !ZI{lk)jqDd%< >e`Ё}:/ZBXz=W3c3gmMc{^X2}Yt{tI]3#I忺9#5*U|YoW,kSU͙VБc|:m6{vl͍~Aq _?S iVwܡLRܾ}xbsukzb.Yi |x [6nEdd M}fHUoIs2J[,v_̞zLtEбM/佮t1IJJ_c5,~]z?`ozԟ~-ܹ[1WXM=GѸax8&-]뜴I͍۔+[&SWs ॲe$h(~^c㳱!!! aiX9ϔ L&E&B[: $&&s<}oAWo)wh5og̹&%%+W1zoͽb]QQDFEق%zm/\ok߲E3>[Ȩ*y+)?vNNI1F z~wR =%"*jhߪ)#kT'],q=V2%<,EW+eTT4s.uz3VnOLL;;[BoeꬹY>Wi~[-=1aHʕ-goScݨym'`P6{>m삳3=}CG ٣t3x]!2{tx;ܿycEQ!ڶSn٣T*ҧ+Ct5X/s.cbz!w2_of]vmZPkս/?E@Sfhg?q|)#Nnok~k6ۓKNj=ެbק6nag {[17|eִfal|F`9||%=ݣ+{i Je˔]&_0o`u~O࿬vlڽv}'+/Ն8= [=}acHi(-%gЗ@9so׊^̜<B<ɑRڡPˈ Mr2Jg[u ~V;h;S]~OC |q/={?qR5y&>NhZa-Pzmt?>fD*zWcպ6?XEEj?I(zIΞtۏOiWZ_@*Ui+S{g8>Vi3f59'ٺy= ;~lL> ʕ9^~Z~m2"gmRiuOzKB|x!NN #~ 9d՗/\Ǐs8~?&Es[V\q*T(^prr6?^yeFgG1..0j22>l(NNN4oP]+RǎI1WW2vH19c Ezܽ_T)y3_=CR%sԟO5#::|4FPIQаAL]]\8yy qҿ<~Xw%&66S^^߽w/2,ݓO׳dmJl ۤIw77u._+VԣjJa~_ IDATi NS&T*6aI]Ns. KhҸjKi޻GRtA?g^v,Q[!!ܻ;)Z=}ѻWw[_o|0|666,Y8 T*1.\T*9qo$0 @Wآ9; ꙌQucHJJbY֝\cbayzI:ÌYsrS|{ɓ8">Mi߂W{z0i4]ΔIx啗X~:k׮3i4H"i' \\]Uj54o+oꇽW+UHT+нWtD^ʲnsۃM3j$=zѣ;gȯ]A`*"3o0}є[ҩ F[`h?n1\qQKgҧ(A F]r+,#e 0_ 99۷:m!e }i߮-&MWPTTŋt1#Iي{:"O[ lAyڇ"B+"IY!$e!"ŠHRB+"IY!$e!"ŠHRB+"IY!H3̮PsQW_*+s\.(x NRNJSsɺ66Ӫ]G<щIsIIZ-,Ǖo}c1M 6Yq&~eZqUM_rV-_*Tb:mt)OsȈOF]}|oy@&jܸqѣG3p@u16[ٓGi2'D=9u0Ǐ 6c^cOsVc#"Xh~~5_`͜'[s1ގ%/9=w>s.` 3+vS&=vnAIӤ<~(܊=:`9۶}n|nŊ1~(/siBCCܹ36l`ɨT*f̘X)wpp %SO3s\_H||< B}ҥGDHNVmeet؁ϗ,eOpw7;v#_ ߺm;sf`ety-N9g/^\%uYDD$J=.]T֓"#=z0sLn߾ΝK.+We٣Gɵ8w8ZCBCuCoͭX0xxsSׯ\Խ7/]bM|b[7n4;e˔a;qttS45\ٲ8::fd:;wb , Os>`Qn|*k3,c}Щx?i2zv~4oFo0aҔ,eϗ(ڵM=EҮm"""ig<)[b]GSF}|Ѫu{jתayu!,Aۨ`b":ksb-0{×2aTO*g:ϴAL蹌Udsk׮ӻoߝ'E #.z~C ΅>|66H!4kgBd0uL BrrSg̢w$BXZIE]iն?jIJJA|tR4|DZ4!r$eFˣBS>u^׫Ec a{O!#IBaE$) !,VDBXIBaE$) !,VDBXIBaE$) !,VDBX-qɉ $%&]_HOFS9xK/|^d}򧂓8sg->~jבGu `1(0IYtT*ubbbزe ]1A&? Δ-S~b9dKx/ 2: VdoݺEnݸv*UB6Qivx]M?Ӧ}'6_MR,6v!B_ll,]vL0Fe;#Ux3iXy[HWĘqVjc$%%e|EoVYG*UNpYҎMeTR&]Az ]GbMߟ~ZqUM_rVfW|G||P II5j˲o3{eLL,u7"6^X5lBlj5ΧnT~&Æ ..سZϴZ7c?g?SsZ+I90}tnܸѣ8p`wy- =;!ؿ7ɝ;w!GcӺ5=yV-;~YҎܯ_h-G"8wW̙ʏ?[=܏`FkN9v1i4sqq_gf\| ?'2*Î_~pl7wq)믷duz׮[Om)ZK/}fN=Jbgs:Zrel35쬵ȾW\I@@!!!zj:tEp'66Vx/;8~ nnx3q8h)Nӳ8/~۸$NNN-SсfMh޳rel~mMT}zb+Ý#ԝ~~ّ昝W2>LW־mkLIDd$L:/S)׮\)hZ4j^"2*gѱC{2_?&L1o{C;&Nߓϐ7ڷӍ9"2)fꕛozṃGX' 9;SӋdqy))){2Æ0: c^[NcvZd_Lʐg͚EÆ )[l㔯MJUӠ1#GEgǶz9"wwZ /OO>hx4}ЩO_ `Z63?y׭StI\]]Ɋ96%22vmSEm䒓!E_sZ} gk=?׭CQzm}L``ѱgdhjr"j &&^޺N=(6C˞1|x!f.@_bB<1i2MDˈ{F\ A!AG g ȯY !)0Iٹ+v4:Z-IIy;(!Ȧ-ȗ CN]a >!ȏ$) !,VDBXIBaE$) !,VDBXIBaE$) !,VDBXIBaE$) !,VDBXIBaE$) !,VDBX|+zWw=gE/ O֔j˄"7>)r9\rV}} !^ >)gluTQ-q*rs6V3>Szc^ʴYӚի}+/ @ǣ8s, C П+2k>B+Iy899ѼYSBBCuei쏃{KhƸ3mʧq#'&ȶ$mK~91ah;'c,Wh4J%aa9'+wݣtRs3^=e= 1r4Gi !2)/ݫ;s-q௃7 [qL_L|diCtt -sȰ\2ޕ+ӰA}T*A/) pN? ׫GwT*Vߦ۫]&Xjd`ezgN¤)`Jxy1xPoܔf?]AHm3j$=+ +zsJfָ֠ M NRNJSsɺ66Ӫ]GXxB|:g *:111lٲ]a Zd4f@R;;;SLiҷOo܊OI.?UQIjuJ ֭[t֍k׮QR%b%x]M?Ӧ}'6_}7n0zh]~ }(>sgN~*Μ[ܱ.Zl0Nӻ'\f`,\FW%KyW .9zMpQZ d_}ؾu3'BR19gnJΞCБ[w~2Kݏ4ӼեUצ|Eo"*4ϗ,EV`Ӎqq)@JsaahՆ)_ћ>S(9gWק624̡xzM=BuGԣ1c@޽)WX~M,Q.[V1r8+ 'c }7b `8ƌϗB GZt-3o̙ӧ0wL/;?oXgqӕ`kkKBB.FG`|Ms2Sϡy@)у3&:fBB/\dʴ۰3X#z?Sjhji3fEdTSϢc$&&RaR C`71t@r=njۅ&p-RRRe xޕ_o'!!pO"49PTӤ1oٲe8N( )W,Mcf o!pV˿y׭StQ\mۼer=c:f,NKO\P_91Ct)_%ҙӧ2j8%Կ+49PmP{P01rlֵt9GѦ+[X܃\ 3rpmt01!ic4{3!th׆Z,|qb87Xa77$)+ϴA&/.ȯYCu6qMڶi#ҪmG:Ւ#/ZҥJx|yY@VIZG,s5?Xԅ|f !,VDBXIBaE$) !,VDBXIBaE$) !,VDBXIBaE$) !,VDBXIBaE$) !,VDBXIBaE$)3Qѻ"_NLLd+u.߸Irr2nnnZfIiIs+(x>)?|p;mZcooǙ+9IBlV%Kp?]ަH888аA}r=^j5~TZXA@6T^,;Zml%vxWE9z츮+9eտBXOʻvC&* b^z߶o򅿍n9{uԱt~s/4x~In^ysz?Ǝ3ݑ핋H7;!eI9""7b&*M'h0LniZ쏃{`Ok2>l(NNN4oPb)<ϗ՟Svww{DFFY"1]]\@VGw6mlvvvԀ3!eIer6ZWVhP*o/OOBo&:RP!ċO_ :W+UbŬ]G'eP IDAT0]2Kow̙ǁҧ߀l)\0Oi<yꏔ)u?+X͘EJJ .E]YWo)L2gMWTi >MWQVM+[q_~E@>^N/ZjBmP{P01rlֵt9GBX1otud/E"IY!$e!"ŠHRB+"IY!$e!"ŠHRB+"IY!$e!"ŠHRB+R`rrbqb̾=~BioYR޹*tHBgV}JNJSsɺ66Ӫ]G~b|q^&TR:qTT%mjsSAc*VaG'06FckojlB< 2)g|!M>7n0zh}[qϽܱ'Oes16[ٓGi2'iY_\z~{w/{?vlߺk/dmW_soɣPTlEa7glBW\I@@!!!zj:theee; bp+VcGe3Ӊxzё &⦷y&M'Ef¸1j6>cmȤ ٙOF~<1ۜ 94tܙ 60ydT*3fQ,c TɒǥKTť))oUߏlj^Mn E6z &vm؄k.)1c@޽)W3͍wPLBo߶yxs+$ _TfkkKBBRsQBw~ްO y汉G;}y̙ xf}wh׆3gMTt4f̶Ek׍_L:{x!S?]Cʯߓۻ︦$@ ZޢWiQTܶκԶ֪WZöںgmgq^G"D Iy$oX{Nr"#4Lo`S+ڙܶ}G}+ -kbL Y;wRk=a֔@Ycr* eﮝ6Ѯrc8k{中Ȉk h?{ 4hˬ)rrrafI3g0v( V[ծB=&UN~tLQ0se,KϿ5qhǪ]!zM(+&sB0__k3 !nO !-PBPBPBPBPBPBPBPBPBPBPBPBPBPBPBPBPBPBPBPU*^^lS[Y?$ S>AS&QXї:KO[\ř~ّboGҼUC !ͬ/?͙ ߏdSS)}9z,EYZoW^oVK@N|tuU;qQs۱af *:izjxբZ4h܌iT}Z&~Tᝥy8y !M2taU2sv(^իoϯYLѣFΟ2%h:r!+|7(&# M~\~a+OeBX6l١]|۷qܩly !M7+W&uu~Zoʤw)I!OOLz7ǎfM+[mZf gͺ t֕ŋY!T\ :w{R ?wwwE֞"gT/oK,ɤw'dX[,Q"ғ|Bx"ωÆǟFY_m;Tu==ΟfLFZܾ5){6z}{fJ^o4e"'hؤ= ?t:]=r8 ? W>1n(*UwЙ [ZkS4({$&v۹m=V]-QByy=\(">w"#1dTb@Μ=GBB7n0sV0t=qy lh߮-SKzjŋo텣SG`סʿ[0rSG`Bak$BE$BE$BE$BE$BE$BE$BE$BE$BEc q,@(&iB/^:HEO( -Ӻ: Ҫ]'?q߶Z8fkpy a7(pp^>5:111lݺ=3sr uΰ Uh4˗eJӴIc OBl/5lKAg+nBhLL7իW\jժɓ'\z[MlٸҥJYmBrٓW2uT6l6ܨYËSн_\سwZiO4mRlٗ˧^ޤ7/uIگRfm b'ac޼y ѣTmUW+Ac_*VjLxo^PAc>[er6ȷ9UkxӶCgΝ_v*TJǰfz4ZMoZiϱ'w۽pE ye6Oo^B gϞ͵kט4iÇq=ueiDǍظv5q*Em܍bi3hˉ#8zh*VdVp(Fg⥘L&s|€~}7y}QX)o+ϥ*`=fߞ_m;~"UCe:ŽUKMz|[`U?B?h(we:qmZ3ie\,yByժU4oޜׯpA֮]/GJEƒ,ӻ{.ݧdLQ>-h2.+ 3-h2[Oz;N>z{o6n?3^ƞg@>9h~… 4eb:~0bŊ9{.{3?D⸺2?bcc9czΜ9k1Ho^š."""ҥ ̘1^Oppڿ{7 4>]1{MN4kɯJQ^tGEE'Nk^K*^xժýnH;j0>^ F0|`\]O(ZheQQє*Y8r]]]ILlOyώYX_|v3;z^y֘dwܧOBBBq 4уrY7iŗ1=(q#nܠpW#njo^޿+qQ5- 8yzv.\7odv Ңֹa) exɆ 68{< aæ-=~lƌ(峂}1sv(:Ox\z\zGD0%hzrFè=j8..ٞ[vm2{s}f ϟWfdv Ғӹ2!)CCClٲnB/*Vλ׻ߦsCu <>^K6hԴ9 CMݏW_NBٹS0!p4l܌V[6ΖйKW_d%E9yMӸwAbn縱ӹ?ye+}ç25dzW7wIYS>gK?d1d(:kCvm&+W?h({wjy"'a vcq!# 4hˬ)qrrafIA& ph&*mΜѣHH0038ͭn^c7'vf%դtR,^ZuvJ.ki/m[`OsnBY1)<~/MںȍoЯ/zy"ݞB[$,*",*",*",*",*",*",*",*b3&&Bi4pֻsR7|(D߽î~p f!L%3NK%hׂaTfl> q*U=hdGFGerF %9~p/%SW<~:eŘH+tJ2z8B(()z$}uo]F|d:q FY! EA1~DRǜh^FLa NX,]U e!SPVONѥ[7^Jn ;<% e!S[) {;a׮\ ޣalbl{BVCz ={&t1;8k -b++WгgO*WLʕٳ'W\1+ٳF-"}IA1PL&V}ݻ+&=wc+R|Ȓ%Kh޼ŋx{{{{{s…"HsuяZ퉾?;2o<J.o.%_|#66SaOz@8P?~<]taذa|g7B8@Bcbm~so09d|aV\ ƍzUr)^uN:EjdBQbtvOjժ,^C>ܵkW̙Cdd$̙3nݺህ# 4OZm(/YCQNԩѣGYdwԯ____|}}gϞy8b!Dv=ol]6m!GˉTJ6mڔnF!((8*!Dnx[7FKΝw|,4INm6SU>[€#o ,ȊePë-qxr X ʖy:e!xi8yU*ۿۄ&V$6OQHsiU*N͇F:Ib-JvPGyz>)ktNPG`4VB%1)&2ʔz>uޕ;Ӷ-̘8QVB8K,i)Z41ĩ6: o(:j;BMLF#8z0|($&&X}W!žH( !H( !H( !H( !H( !H( !H( !H( !H( !H( !H( !H( !H( !H( !H( !H([W-xa)K'!rNwy/3EOLKѢEhdY۱af *:ihcmnW:#Gm3qFXz -ZQû.;e+D8, 'OzzjNU~evls=rQ&eæ-̜j._f-ߟFٳk'?l… ;Or̙ׄKzu9q]̼IznaILLdoXÆY?f4KѣFΟ2%hzs5VePWgߞ_Yr;~iѱL('g8;3y⻩{Bʕpqqsڽ\|ERpM+ ɩ^nM~%Jij\(F+`u9weiۦ5kmp֬@n])^X3kz,=ǎfMi땫2] yzRӓ)R (T/Y`0nFfafp(׮JQ#cO0"y;!sۑ*Y21Ν(N +WJQy-ZM[,bFOAFF f=KKA Ggggs}M)Y =r2uygfRñ'X0ogÎsc@`(^,iy Z\]]yIo)ʒ8oyk\8fQREZ֭ٝ"EYϒc`ma|v=!B`0qs ʕ*eRLF#`00 Ys&&!s榪Z}}V˘oe`<|XCϟ->^8KOe7K<~L ߯je3\||6aG2}TTҦCg*/N^oYȼ!#Py Za }!*",*",*",*",*",*",*",*",*",*b7G~FyP+G~<^vg&n"'OoohėV:ALPŋ縝ծ-c"݄}{`z4İuVzɁj963 r+$n^@N"s_vFcbN^r UVhL|ɣB͞rFCZ RjLo\>sFMYJF#s}D^?'q.C9?dgڵkL4Çj,ѣ+?cOeDIDATslXGôjϔi,w֮":uhGACٽ{/kVԉ#mӚI?#ؿ/'}TXY\|w)oEQ]Kً=?nnT -NQv۾sߒk7 e/|gϱ}۷?^Oȿex\}{>ayWS.]ʦM(S k׮4mڔ5k֘O;f8V!!iԔVSLZNQCxx ..ZuťS[f4GanëVT=hԴ9O}Yk*V(r|21K]ArzowCѢE=/?~8}*K>e.6m7˿R ܍m9{U|q>SNA.]شi3f@2 hJ,i~\TTuCՕs%ori=|'N$d<Ξ;ϓ'Oh2(ʖ)nyVQ]Kًʔ./rfl_.7""#ТUev\}>}7hРOG{m ͛7nݜ9f<}=\x'(J)R¯Gزs̮G<Wč.\(?nXUyC(^:o2BsaÆ;k9ܻ{3+x*-P ?^j>WL iovČ};1sv!;lV#=fSl͵xblXMg~ޟ_$&&rEƎ`.ϭ?O2!)CCClٲ/ pwwaf hO:qvvvD]~~Nlfϡw]zǫɴqcFQb%w_ J>`M9f^K6hԴ9=~l3%s-Z(׬dm,^)Ç~Ww526_["ՉO/djnܹj0> 2}΢\+W?h({wv3amr/ 4hˬ)rrrafI3g0v( s+5k [c7'vf+c)]~EsLuL5vʊIÜ5 ![hמ1nO !-PBPBPBPBPBPBPBPBPBPBPBPBPBPBPBPBlPYUCYr?6&ɚ !( $th]ZvP ϋ/Yƚ !h4}#JZmWz=qkjYa״Z-f׬ڮUCYF݆lR!BPBPBPBPBPBPBPBPBPBǬ꣘F>рFEӡr; e!b2C䭛y=jyHQ4lGhU/PŽ>ef|V8~hGW\Ocb:tFŘC9:u_{Ka0$9dBI9ۜtNM&0s\AEQz6eA e!(/%RDNI( R6ӏ|0l^!khõigtܿH( (, 常8-h!6t.sFѢh4`2)HGI(I{G߶^ { B8% +ǏQ0 Z'hA4<={Jr0'tĤ5 GG텄@AAQ,D_\|:PLh(IʉI6S+FFԦ;: e!AV&˾L(~/4|BPD4`JDS7$NE ~2ј?ʔkfuLHGl1!i+*xt7iCuHڪ0$kA6ʮVdǑZ%P$m_XEOE}$)]M)2P u^cŴv$tTR iu?:bԣRؙ?bq",CPN4\?rl5O(SJFtyQ¾ؤ~RN+WҭG/ސݿ™|4a{q䟗EBYJ5UabU8 y(v%(D?I}巏?u }̈CV<< ӥRg]u4%xs{hѪ դac_֭h.KLL$/zb<~XVBSqʭs(׎3I6)Ӫ!IivѮm{- v$7o1Bsp&8֭˖~gΜmqz=!seOY7R~Vu ӭsp/ܼӾG߷A)\.̴)ls^Ν܋G =\afO}bŊ{~٩ʕ\'0IL0XTטU_OYsFg;~ZӒvi (;w(R ǓhJ/f~\xQ/Y8.cKG4=|yڤhWX|L^& e!@VV{ESe҃v/@; ww.GUB8|O抛[)r:9 e!I( ^7Pži#=+De:C e!Fx\'+T`Φ׮RTY4l^B1Ή{8 ټ‘Z/AzѻzB1VKAOOZhG1t_iN'}',hu]ѻPKBE$BE$BE$BE$BE$BE$BE$BE$BE$BE$BE$BE$BE$BE$BE$BE$BEzB!iڵ z,Bᐢ#o0fD~?pHc޾HLH1 !CJKX 󷽚n]QL4ObDGaL4~@ݳ(35/)ā@B9Y>KB8J , _IENDB`help/C/gobby.xml000066400000000000000000000404121304552132100140110ustar00rootroot00000000000000 ]>
&appname; Manual &appname; is a text editor that allows multiple persons to edit documents collaboratively over the network. 2009 Philipp Kern Philipp Kern 0x539 dev group
phil@0x539.de
&appname; Manual &manrevision; 2009-05-10 This manual explains how to use Gobby &appversion;, a collaborative editor. Feedback To report a bug or make a suggestion regarding this manual, please file an issue on https://github.com/gobby/gobby/issues. The application &appname; and this manual are both licensed under the ISC license. They are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warrenty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Please see the full license text for more details.
Introduction &appname; is a text editor that allows multiple persons to edit documents collaboratively over the network. Changes to the documents are instantly synced to other interested parties. It uses an algorithm which does not require locking or a central coordinator but instead merges the changes on all participating peers. That said, &appname; is currently relying on a central server that relays the editing actions to other users. A public server can be found on gobby.0x539.de. If you want to host such a server yourself, please look at the infinoted daemon. As it is not needed algorithmically future versions might be able to do real peer-to-peer communication. Support for editing over the instant messaging protocol Jabber/XMPP is planed. Depending on the setup of the server, connections can be encrypted through Transport Layer Security (TLS). There is no support for user authentication yet. &appname; is cross-platform and supports so far Microsoft Windows, Linux, Mac OS X and other Unix-like platforms. It is developed on Linux and regularly tested on Windows. As it is developed using the Gtk+ toolkit it integrates nicely into the GNOME desktop environment. In contrast to the older Gobby 0.4 this version also supports local undo and redo, which means that you can undo your own changes regardless if someone else changed the document in the meantime. Getting Started Starting Gobby Choose Applications Internet Gobby Collaborative Editor (0.5) to start the application. You may also start &appname; from the command line: gobby-0.5 Main Window The Gobby main window is split into two parts: a document and server browser on the left and an editor part on the right. Main Window Setting Preferences Choose Edit Preferences to open Gobby's Preferences window. The window has five tabs: User - personal settings like user name and user color (see ) Editor - text editor settings like indentation and auto saving (see ) View - control how the text files being edited should be presented on the screen (see ) Appearance - font and window layout settings (see ) Security - controls security parameters like trusted certificates and encryption enforcing (see ) Personalization Preferences: User tab Text Editing Preferences Preferences: Editor tab Tab Stops - The Tab width setting controls the display of tab characters. In conjunction with Insert spaces instead of tabs it also determins the amount of white space characters inserted into the text when the tab key is pressed. Indentation - Home/End Behavior - Smart home/end controls if the Home and End keys should move to the first/last non-space character before moving to the beginning/end of the line. File Saving - Text View Preferences TODO General Appearance TODO Security Settings TODO Shortcuts This section documents both the set of shortcuts denoted in the menus and those which do not have a corresponding menu entry. Gobby's Shortcuts Shortcut Action Ctrl-N New document Ctrl-O Open document Ctrl-S Save current document Ctrl-Shift-S Save current document under a different filename Ctrl-Shift-L Save all open documents Ctrl-Shift-H Export as HTML (since 0.4.93) Ctrl-T Connect to Server (since 0.4.93) Ctrl-W Close current document Ctrl-Q Exit Gobby Ctrl-M Switch to chat (since 0.4.93) Ctrl-Shift-M Switch to text document (since 0.4.93) Ctrl-Shift-C Reset user colors in the current document (since 0.4.93) F9 Toggle document browser visibility Shift-F9 Toggle chat visibility (since 0.4.93) Ctrl-F9 Toggle document user list visibility Ctrl-Shift-F9 Toggle chat user list visibility (since 0.4.93) Ctrl-Z Undo Ctrl-Shift-Z Redo Ctrl-F Find Ctrl-G Find next Ctrl-Shift-G Find previous Ctrl-H Find and replace Ctrl-I Go to line
GtkTextView's Shortcuts Shortcut Action Ctrl-Up Go to the beginning of the line Ctrl-Down Go to the end of the line Ctrl-Home Go to the beginning of the document Ctrl-End Go to the end of the document Ctrl-A Select all Shift-Ctrl-A Deselect all Insert Toggle overwrite mode F7 Toggle caret visibility
GtkSourceView's Shortcuts Shortcut Action Alt-Shift-Up Move viewport one line up Alt-Shift-Down Move viewport one line down Alt-Shift-PageUp Move viewport one page up Alt-Shift-PageDown Move viewport one page down Alt-Shift-Home Move viewport to the beginning of the document Alt-Shift-End Move viewport to the end of document
About Gobby Gobby was written by Armin Burgmeier and others. To report a bug or make a suggestion regarding this application or this manual, please file an issue on https://github.com/gobby/gobby/issues. Please include a reachable email address in your bug report as we often need to contact the reporters for further clarifications. Mailinglists There are also two mailing lists which should provide a means of contact to other Gobby users and to the developers. obby-announce: A moderated list used to announce new releases of Gobby and its foundation libraries. obby-users: Discussions about Gobby's usage and installation problems. The announcements are also posted there. More information about how to subscribe can be found on https://github.com/gobby/gobby/wiki/Mailing%20Lists . IRC channel Questions can also be taken to the IRC channel #infinote on Freenode (irc.freenode.net). However, you might need to be patient due to you and the developers living in different time zones.
help/Makefile.am000066400000000000000000000005461304552132100140430ustar00rootroot00000000000000include $(top_srcdir)/gnome-doc-utils.make dist-hook: doc-dist-hook DOC_MODULE = gobby DOC_FIGURES = figures/main_window.png \ figures/preferences_editor.png \ figures/preferences_user.png \ figures/preferences_appearance.png \ figures/preferences_security.png \ figures/preferences_view.png DOC_LINGUAS = help/gobby.omf.in000066400000000000000000000004131304552132100142120ustar00rootroot00000000000000 manual icons/000077500000000000000000000000001304552132100121655ustar00rootroot00000000000000icons/HighContrastLargePrint/000077500000000000000000000000001304552132100165525ustar00rootroot00000000000000icons/HighContrastLargePrint/48x48/000077500000000000000000000000001304552132100173515ustar00rootroot00000000000000icons/HighContrastLargePrint/48x48/Makefile.am000066400000000000000000000000271304552132100214040ustar00rootroot00000000000000SUBDIRS = apps actions icons/HighContrastLargePrint/48x48/actions/000077500000000000000000000000001304552132100210115ustar00rootroot00000000000000icons/HighContrastLargePrint/48x48/actions/Makefile.am000066400000000000000000000002321304552132100230420ustar00rootroot00000000000000icondir = $(datadir)/gobby-0.5/icons/HighContrastLargePrint/48x48/actions icon_DATA = chat.png document-list.png user-list.png EXTRA_DIST = $(icon_DATA) icons/HighContrastLargePrint/48x48/actions/chat.png000066400000000000000000000035001304552132100224340ustar00rootroot00000000000000PNG  IHDR00WsRGB pHYs B(xtIME0!;IDAThՙ_LTǿ`֪"2R}dy)1+X 5av1ilj5F7dڇ@:(D]&cSH71;#ovͽw. L̹wΜs~F`Br3}@ :(`i Z <".h #I0 G1.H54xtH$L&Jl6p8PZZ ݾVcB<~$K}?H `dd?˷b ӉZԠ Ǟ={PVVf4UnR?Bp%>%djj,--%ZlooW?FM$cV:3gh67b452£YB$-hˡCpV---8p\.jkkQUUłʼ;KKX^^b1$ID",..ӧB䤢'ˎ;p]TWWoxOIJ$ BʣGI6ARlݺldY)LzQ ===l&!PHB\iG"MYn-..*=N cyeeE߽IR:dR驨( f l$P"/#c )%D@ b:Bb(6dRƀʢ3@=%/J[uuuJLQ $GNYBxA(?wܩWLɭlթf111QP~&׫|W'N(©TwVX?ny(B0l8|"`GG&5H*%w3 |نAz<h=j'uFVpm)GCCCCAcaap`h4OgzqBo4Iǣ(m۶+p3cF)OZ^W !xҥ5OLLe]|]]Oxxn$B:۷ǎӧz877~:opO%eUUvW}˓'OܜJuJ$@[?) e xss3GGG o$FoD $i ^nݺEͦ^\NC$[H9I>Pi_Nժ[,^|3rU e7+ QOOM&WRRA=IJL~ZSvd__yw Lܖ)ŋ900w^ P[s333x4knz2X&9{ll6~='5 6`Je\.Glv7:j544UTj7oK'd$pb7?/dmmmy\vM BbrU:zzz$Ud2++<ܹsj ]7f__>*$_"#Ɉѿ$E$w?a&IENDB`icons/HighContrastLargePrint/48x48/actions/document-list.png000066400000000000000000000020561304552132100243110ustar00rootroot00000000000000PNG  IHDR00WsRGB pHYs B(xtIME07 6IDAThOH+Wƿ hb"Zq.nJWnZqS()HAy RZ"ծH N( ֍P5I8/1 LΜ;sfD@E$ JB5UfH4!9%7fCVV,?(px\ڊ\L=~!"ݎnJH_*.//u >}V !l/ @'AR$oR(nG4}dZ'Ieb{{χLW?H~ өY|4sss7IV @ B\ߓ4 @ jrcc#WPf.Px\s:BΊ.XXX@]]]}}}Y) Ɋ3!īF?22RHLLL(ݾΌTleYiYA9n+++Y-[SYӰX,%vU[V{ZRTlFss3fwRzzQi(i b1B!? ,--)xeߟ7QSSeL$ɂx. ncvv>I "_cc#l6r)Ds(q 6D"444@8<<,S(JJ{f,&LMM_CCCEgKMN&1l6ݛsx0R#K&6,Ԭ9_W,FvUrkgoK_x:M efjKWOZq^d1*H~\gHIENDB`icons/HighContrastLargePrint/48x48/actions/user-list.png000066400000000000000000000035441304552132100234540ustar00rootroot00000000000000PNG  IHDR00WsRGB pHYs B(xtIME0+IDAThY_LSYn; HmPYC6$nf&1>1FÃ& ` .fdb?1!i0 043o_zmK{;3${99WFaa%I"~/FcHld5p$'I~GrE28g;OMMa``X,p\0KkpT_k$; yEn߾bJ~~>kjj=~[~ fFyed21c@2'[VTTh.;wА3 g^lB!ݻ݂nGEEvՊ tuu>Ou8zp\H7GH'ksaD322X__ Iϳv]o۶mQH֛ ---vMsׯrn&e9$Ilmm]+W 055%,vz$9ӧUUU% q.zحz!" )9q |g9i^^ʱigP6uaaeK/YYYIXZ-CAdlw#an4aZ̛ ~ecƍrիWF x\˃`X2o2~T6:$GGGq{^]^^޼B)7-~A=<#ȲgJa:~ rI:\~a!yMMͲ_~]ӦMWL.lt#Тz1D}}&\b@ 77###jLIeeelkkc4ê*#eW/[Y8pţGɓd~~MNiKR$*$ݿ+b0xٸ񪫫O5Q🐔Nb4e__4ߵk?~8$S(JMe} <OΝcii)sssAb>}=>Ѩ vgKRØc2:fSSmr,%{ۮ]&زeڥa9cZZb0 N sÆ ,hB$ wbnnNNZFdz49sF"an-G%mɋ/* .**RE wX,9fcm֭”Zjiy回F0m Ŕ9H [Xj!X:SN`ڵB{hh(ྏb%==]D")'0;;+M&S\l#\yqz{{SN8n0jj?sLݬQURRvV -ˈ;wĈW۷cbCC-_JH'FU ͛71rrr4X8X} b999p8Z0L00 C8|>:;;ݍ6 <͛H5%7oް8I)Q?קrYFwtR?<>465w֭[>IYFdeeaժUp:p8p&O$gW|ԛ$$I6$$4,4;ؾXXca @$I?ւIENDB`icons/HighContrastLargePrint/48x48/apps/000077500000000000000000000000001304552132100203145ustar00rootroot00000000000000icons/HighContrastLargePrint/48x48/apps/Makefile.am000066400000000000000000000001621304552132100223470ustar00rootroot00000000000000icondir = $(datadir)/icons/HighContrastLargePrint/48x48/apps icon_DATA = gobby-0.5.png EXTRA_DIST = $(icon_DATA) icons/HighContrastLargePrint/48x48/apps/gobby-0.5.png000066400000000000000000000046341304552132100224330ustar00rootroot00000000000000PNG  IHDR00WsRGB pHYs B(xtIME.-}Hn .IDATh޽kLUW^,h2MijA;*-jiUƤ j:mt2IS&|MӴF ڴ"ikQbբcA% *vryykIT&ɣ$t%j< ( Qx< dپaTMLs5440gٲejhhDSo.}фv}AI3 `6p:c^ KUU=,IZmFx%)]_%e<7MTPP`Y0//o\/fT+IU IU5?%Q|Sssx@SͷO8zJ֢K,I?>)0 C~WVwS %;;Xv p/^aҥ_ݻwS__޽{z#g`vIxx5VXX+nSRRBff9o0VV֭[p(??_v}׊_nݒ$;v@rrMz0 ksT-IgϞ ݮCusss-Z$@5oKV^=&/}\.ΐBf)ƦM(..歷OfŊJPPc۶mx<7F 'dggt\XXmmmn\.5eF_j*@.\-\P~'Iҹs&Ս(vtt3bcc~:8K%<N6n{DZ[[1I3d^Jaa,d۶mcҥJbL̼ywttqF9Yj9ygFDž$]{OxA֯ өaVTTʅn۷Y7`-ŋ/3g ـ,_K.QSScpE|3͛7̕$''SYYɓO>'|Bff&Ñ3gZciii[A߫$5Iүϊ0-YD֮];bرcPAkkkajoo.D>IjjjR^^u1ͦ'O|mmf̘!@Og}wƻ)++… ٳ{RRRyuq5OGE.]BkHBBvu/4 #_8͗T$}g,p8k׮]cq!\.]rES$͒"F:$ڢk֬QNNU>ׯ?hzG image/svg+xml icons/HighContrastLargePrint/scalable/actions/document-list.svg000066400000000000000000000076011304552132100252740ustar00rootroot00000000000000 image/svg+xml icons/HighContrastLargePrint/scalable/actions/user-list.svg000066400000000000000000000157611304552132100244420ustar00rootroot00000000000000 image/svg+xml icons/HighContrastLargePrint/scalable/apps/000077500000000000000000000000001304552132100212635ustar00rootroot00000000000000icons/HighContrastLargePrint/scalable/apps/Makefile.am000066400000000000000000000001651304552132100233210ustar00rootroot00000000000000icondir = $(datadir)/icons/HighContrastLargePrint/scalable/apps icon_DATA = gobby-0.5.svg EXTRA_DIST = $(icon_DATA) icons/HighContrastLargePrint/scalable/apps/gobby-0.5.svg000066400000000000000000000605011304552132100234100ustar00rootroot00000000000000 image/svg+xml icons/HighContrastLargePrintInverse/000077500000000000000000000000001304552132100201065ustar00rootroot00000000000000icons/HighContrastLargePrintInverse/48x48/000077500000000000000000000000001304552132100207055ustar00rootroot00000000000000icons/HighContrastLargePrintInverse/48x48/Makefile.am000066400000000000000000000000271304552132100227400ustar00rootroot00000000000000SUBDIRS = apps actions icons/HighContrastLargePrintInverse/48x48/actions/000077500000000000000000000000001304552132100223455ustar00rootroot00000000000000icons/HighContrastLargePrintInverse/48x48/actions/Makefile.am000066400000000000000000000002411304552132100243760ustar00rootroot00000000000000icondir = $(datadir)/gobby-0.5/icons/HighContrastLargePrintInverse/48x48/actions icon_DATA = chat.png document-list.png user-list.png EXTRA_DIST = $(icon_DATA) icons/HighContrastLargePrintInverse/48x48/actions/chat.png000066400000000000000000000034311304552132100237730ustar00rootroot00000000000000PNG  IHDR00WsRGB pHYs B(xtIME&3OIDATh՚_LT?3Έ3 0]۔vp4+hV҈ щ@LԤuLվb oM(!6RLKBBI[P*j{B2o2!߹;  pyxTƀ? !3  fWJg-*"]JO[zj^/6 Ś5kx<̌v昞^n'.._7n7{% RZZJII EEE׫M&LLLDcpp^>nXuuz/555 BI'Xqs`rzb2q%P䧁#@P6 ٱca4Gx<˩S>R+-jǏGFF4fI&Zܜ:t >DANW\ FA3߀ ËgJCCaFg3|2|-2dƍ c~jjJvmd8 sŴm^U,j3|_VLj,#5Άh TSShU[[ked0:zSݿ"[. ~~~^9bY6.pi;vLR6] mrبf=|$۲wvpp>4D*H,%8#\?Ӽs;Lr:WsIENDB`icons/HighContrastLargePrintInverse/48x48/actions/document-list.png000066400000000000000000000020301304552132100256350ustar00rootroot00000000000000PNG  IHDR00WsRGB pHYs B(xtIME'!m IDAThKH+gGJH(xcB*ޅtU J馈hႠT\M v!^Eh݈tQ(Hu!DƠy1ҤIkq>.*///<NS鷚.ؐHsssQuPkkk~Ƚ>ۇEqvcjSb!"p\#fDfUX^K{n=@AKjp8hiiQψ68^^*666;$0::Z9Kɔߧ$˶N'+++444d;??'V@ P<ѧ hy\BE8nlv788(|>\/www+g?^<'`osYvV g;oQ.!sYTIENDB`icons/HighContrastLargePrintInverse/48x48/actions/user-list.png000066400000000000000000000035161304552132100250070ustar00rootroot00000000000000PNG  IHDR00WsRGB pHYs B(xtIME'mIDAThZmLSW~nU>)5l1 ?fقH$ j/ _FdÌ%N2-,ԥS*ut,Jt% w?ޜ{[ල$ܞs9{AY1.[%`(G/V`Ps.-Kм 9_ \NNڵO&t:jD6N[<ϧ/Roo/%AlTRR8w_xtsGϟp8LrիWJFJbq ͥwR*2 R2݈rc? BluBWW\.BZ-, ?Q<~|)=577'ZT*544P {)mܸ7< 4`jjjD 0:>KKKibbB4E~a x<8T]]7jh;$rLb^{{{*1jmm驫٣Xŗ.]޹sgJZ- znݺ%ӨӬG FR+txB59T2ƶmۄDZ33>>.ԋшܢb؆V333ibC:)L`0(7oN+**HPhEgB}PT)رcP+Moln B9r$eB{UH&zD"v)###ۢ㸪wnq͖SN{^RTlhФtK<ϋ܉fYhqqQD`dd };4\t8 }|Nq\pD"<{,t<c3YRnѣ|p\χlFYYٚ3ׇ}aaa!h@)|HKKK=x nI%H^Pߍ7onOLQyyyЛ7oq{n?~8s<;vLJ< jҕ+Wp͑磞~:rbB!AԺ>ё*]}vv6  !k8Q 9JXu'sq*|քy2?/ 23ם Dm/a[l=qL&ӺX,Byx^8n;wT455l9NiOc݈'N9[gΜISjkksI}c222vyNֺZdeeɓ'qGGG d=b{N%%% ~?BD_ʠuuuQccJ{i,7K\^^d``@^^rss/6`ff]5@ 0?ϒAMJ*QXAs|mJRQ]Y)-_z9_{M{3IENDB`icons/HighContrastLargePrintInverse/48x48/apps/000077500000000000000000000000001304552132100216505ustar00rootroot00000000000000icons/HighContrastLargePrintInverse/48x48/apps/Makefile.am000066400000000000000000000001711304552132100237030ustar00rootroot00000000000000icondir = $(datadir)/icons/HighContrastLargePrintInverse/48x48/apps icon_DATA = gobby-0.5.png EXTRA_DIST = $(icon_DATA) icons/HighContrastLargePrintInverse/48x48/apps/gobby-0.5.png000066400000000000000000000045541304552132100237700ustar00rootroot00000000000000PNG  IHDR00WsRGB pHYs B(xtIME'8IDATh޽mLW ˼$@x[5m)FDVƴԒ~`m5Mܘlڢͺ[ݕZְ!a#Hk@еPgv &p39i}@l9 yMp$o* BhH j ͕KIIэ74[illTJJhOt z-KeFh&R"l"?Ρ!n'lX,2228<7ofxx`9w`=FwVa,))-**3穨Phho**[& ;0ןxqXXhѢ޾}{Pq@ ) 9ikkۧp ˗`<kY;QNii)9 % ;::ϧ JKK ,Y{bX&u G+**ĉX,|>TVVõ@nv(KKK%IGRRREGGP;wpкuL\reZٳ=122bcXm&@+V$y^mٲ%&I裏Ç PMMʹ8q":Nf\}9w۶mȑ#~ժU100`hhݻwsiV+wS#&JKKzhtb\.caj'Izg&p풤<zJ&e\"**h:;;'͛7Xt)82Jpf*p80I#d/_NQQQ,$ӧOT02S__/Ia(>>^^~ejjjzB| >Ɂ!!`jkk{.wd3 !իW|$%%q% hjj2?|駼+fp-زe ;v8sLC8uuuIY}>TVV*ӭ>OCCC&EFFh9i(//$(77WǏ7-uuuzvZnI<BvXfjkkK/dr(^x.]ʒ%Kxꩧx'X,rMz… yąKWWǎN%.\ I:sCHMMUKK˄Ybh```G7xc!T (66V^W9F\.I۷~hzuus>-X`Af";wrU[oNYYj6lkظq#w{Myy97ߐ̮]B=.\2J1%I<ŋ'l6Mnݒ$9rd^Ѐq11\.9+W7.dNZZ/O? ?0dW2w[[[KCCYYY԰o>*++x<W… 3.]DLL/x2/tPoosSkk$jx=z蘂fZvc722A(11Q%%%ϧ OMMU$g̠^W[Mh"6l@ff&$$$֭[|>cTUUl2^/Vʕ+nLexxf|>'MJvϞ=c,x_hzuI t*---nnnҁ/ɓf/_}V\NIRCCYԆUUUl޼fVX&))m۶MXX ={v#<3()|\(++ hv!IRGGŒ n@%%%i;&&Ƥ6m2/·6$رcF1_J;fwxXVp:x'9&yӕA2')h駟kMzje ><USi` z.Tr8דj[p:\.vbcc`p)c̛Os.6qGt)y image/svg+xml icons/HighContrastLargePrintInverse/scalable/actions/document-list.svg000066400000000000000000000075741304552132100266410ustar00rootroot00000000000000 image/svg+xml icons/HighContrastLargePrintInverse/scalable/actions/user-list.svg000066400000000000000000000157541304552132100260000ustar00rootroot00000000000000 image/svg+xml icons/HighContrastLargePrintInverse/scalable/apps/000077500000000000000000000000001304552132100226175ustar00rootroot00000000000000icons/HighContrastLargePrintInverse/scalable/apps/Makefile.am000066400000000000000000000001741304552132100246550ustar00rootroot00000000000000icondir = $(datadir)/icons/HighContrastLargePrintInverse/scalable/apps icon_DATA = gobby-0.5.svg EXTRA_DIST = $(icon_DATA) icons/HighContrastLargePrintInverse/scalable/apps/gobby-0.5.svg000066400000000000000000000616511304552132100247530ustar00rootroot00000000000000 image/svg+xml icons/Makefile.am000066400000000000000000000001071304552132100142170ustar00rootroot00000000000000SUBDIRS = hicolor HighContrastLargePrint HighContrastLargePrintInverse icons/hicolor/000077500000000000000000000000001304552132100136245ustar00rootroot00000000000000icons/hicolor/48x48/000077500000000000000000000000001304552132100144235ustar00rootroot00000000000000icons/hicolor/48x48/Makefile.am000066400000000000000000000000361304552132100164560ustar00rootroot00000000000000SUBDIRS = apps actions status icons/hicolor/48x48/actions/000077500000000000000000000000001304552132100160635ustar00rootroot00000000000000icons/hicolor/48x48/actions/Makefile.am000066400000000000000000000002131304552132100201130ustar00rootroot00000000000000icondir = $(datadir)/gobby-0.5/icons/hicolor/48x48/actions icon_DATA = chat.png document-list.png user-list.png EXTRA_DIST = $(icon_DATA) icons/hicolor/48x48/actions/chat.png000066400000000000000000000052411304552132100175120ustar00rootroot00000000000000PNG  IHDR@@iq hIDATxL?UTU.dցAPabQlen5 )k4t9f-6?. Fc6&C YN˽gz "={ssy@,؍cnm8R(>2Ϛ<|ҀnJȨ*3@>,`3Z,^hv;111vv;XVhkkpsu}(u_wDžWMz>^ߌ!33 222HMMeĈ)ŋTWWSUUűcpy- ȘT^PNR^W=jVǏWj̘1J 2 Drt:%.KXNц_N8I<z7UBBI I6Uiir*Xv{hsX> f3;;[9*r5ej_сbQ *555~vM6)ŢIO*~5k֨(uiu=V&o=СU^N~eeep}/..1z>@۶m ͖-[t//x+)rrr jVZG/P׫-[KBsߏj%~ lo,`I󉉉!$ :::vSLaڵ7E@ZXX7o&$<*5s-5ie;~2Z hy&۷oח'd|zIcUw_twc;PAX`5@AAA@ nJ{{;b:76,z}O{7 -oiiif8weŊzW \|?VLz"Cр]/7nPPPW x1n"2Sѽ^l~&CqNΑBHv*33^}ӦMx.=\`8C&<=ETVUTT ~ΝvX%Px(~9=|;)>Ԝh gwy_%%%}Az(LzsDCǃZhQq"S?~ Γ2y>!PDUUU4SQshwDlUnn}<&Md70 H7䁢1.Fӯ^IeddP$d ^&CJ6{;wTJ)uel6c;*x8TGw@͚5K߿_\4o4]yTĞ1>6?bf,d#C[ߓyc֣PY/eIENDB`icons/hicolor/48x48/actions/document-list.png000066400000000000000000000062221304552132100213620ustar00rootroot00000000000000PNG  IHDR00WsRGB pHYs B(xtIMEXE $IDAThKl]]]~_C|cBXRFF0b"&bнb._6h6`fXD6hG6vnUo6Uv;$$Oծr?|eG=BB|S˲888e_lV|<#|k׮[oq֭[ lL\.72lek׮O?TBQU<(0  /Q'l0 Ldz< Bf7@:wIHDgaaZ,S,y&.\`jj !BlۦhP׹z*^a:9;O\nq5W_f!hZ|b1 @QL$NBPh6d2ϓj5FaZ>㻎fT*E$!NJYXX7|({'?Qgh9¿ y?N4Ų,$=mcY/ovpnnEG ̜3_@UUn޼Idggj @44M`0x6M۶  wEn=otĉ3<L93|Z\\?}矩jb1áK nܸA*²,L4MbOƶmOJO?M^>sΟ>  `}}UUZrƍ9 4d2I'#IgX,˔J%,"i?^xAOT*`߉NaiJs1>yEQHRDQ2 $˗ygm@ `0xH?{B3`9!9>F1ZM'2x(#B7MIt]dY& i~*B.s/N.NOѠRP(X]]!lEQH#\.,,,$I vƏ{ &@X,z H@T*HXdqqqD붷 c~~-R C{fggXdN 0SUU677ZhЅ,b~~ 2 [[[E(Jz!{FSG%VTzbʦiΜ9C?Sc&333HDXD$fgg BHp8Q4f!;=nYzn}]#&v7n߾M>jtftp]B6`˾}a~B @u&ǎc{{{b9pmX,F8&JeY̰aD"N8q7X.7ҟy=yG9~8eq曉[Vr9$IB$.FX,ɓ'Bfy7L$Ƹfd2I:&iPh@cee49qkkk#Bw$ӧO: ~9{7o>lqQw㶻mZ^G-h4ʓO>(츞MnnE:n8 QvmvhBvww4mOH$B*RPUO<xB@\&Q,IRr¤x@;;;z+揄=}x= AVZ4fnK2`x\Ća/N!IGqd2Ν;i s)N)ߧi1װjJ&AS,BP(eYhix҆Ovnw,N֖v=~uvvv0 t:MZ^sCZ[]tD(='8ař~!nK\(r˲PUu|d2b1~'rrd2f$ dY>'M}۷G:U_0.[[[GVz)&%H$B#J!2ƣQ^N[;8j.--qi\|5:rb1qےx'5ǚVUUU@MhlYV|[˭-,Qd2j5.'O<bx_nNw,pQ\ XE\fzz4=dY$ey, ! ~) {i.E3Eբ\.{1ʶCvV~ :p{Q0Z-hC(vT4_ ]|~p^x}8wk9W? y5~+ۘ_z76uZ<#>l<9^[4{~+!6SIENDB`icons/hicolor/48x48/actions/user-list.png000066400000000000000000000052611304552132100205240ustar00rootroot00000000000000PNG  IHDR00WsRGB pHYs B(xtIME!+ CIDATh͙{lS?ׯ8N;! @@RcݣimWm*6U[=*4V u@;.P&<Iĉ;so<=9N|w/\-@P@h h|F<ޔzXE&d ~U|.<ϺC] 5̿F!l~c } +בvM'ـu o`TVgxsʲץNML!p.p'2`T8pjh# q5NOIOI.߭4OE2^6|s"MIKpz4%Ҵ Kn+]NdL(?m,e)%:֘tR;B~9E &Pn,MFN ~h\Q-.q@D&ȄMȫ!M %RH)GhJ4g~.D)" UJ6ɌLtЙaF~E9>.ρ?'0] *U_emj)⹝T;Ϻr3 pvw"8CMSgޏްnO)'<~/M?)9pT07$}lʇ':2'T%N~ؠ4* kBK]8MAӖ-7g:=E)snt8RǁWtr-Nh|9) ΚۜNS`sXS_ V= %xV_$6ݷK,\GhL}j9ɒw:bסN.F-Xgס |KL.'vsYUJyo^- mebut8q EaWe: aߺb={xH@V7,k #`]ߞy~_Pa{9$\cwm7"w8TU3硻9teli0skT|Ty_`A4&)e8eTXTRAZB5o޼s͖eK_=s;ZSydrsp8]PGHHS3Gh%;1?把ORr O=TRjbR–H$N$u88:BӔPRH ?˕ ]KKKۗ.] ܕR|jhEJeJi===`{{{N4͊FxoN0|텶zVek ѮOLmwO7źP(`Y2:S@8k<\JɆ6PWWf?Xѥl<9BS9 ٚG>4P(eYg|w&qg΋a$QJM_f̓SCK̛Z_k}JyW.2~btϓ~rX켞/d~?UUm"o5wojlq+`RGeoĻ|AT~+Rn <0 [S Fzv⊛5w͉.Rpdđds#'/Be4:M#|̝{9/S[[Z^y؜"re\E*EBx8;_sx"!⚆SJC$ ٷ?$I䬯[oA)E6ct_uQT玥rѧ]CCιbժU 444EO[ҊgPWW{zj,i.OW{{;ǏHcc#gX95bFBE$~'A)#ÙD٧O!"8By ]=2-[~z Sf ye-;^cdxӛUgUU{ypb*ˡc@hko 4ꑷJj*yì_ZmKNmlÈC:!NǡϢQUU緰 /.2Z9dRVco~+/RdՑxMHW OKI 8.Ṭv$CN*yңNDkb <8G"OG-K9V:OzTސ$ R[[Cs  vgF&`V ?EBI. D"xRw£3@u=A__߸9uCE Qe&lUV|{ϕu \:0#c̟wI뱯'OBs0M40 0Ks;iI"S _&MW7͚P.Kd>$U, LWv;,u1d$Ǐl24M;s;. ZR$<}/iN>)eapױ$Czq3]m pw,sqyاQBZ"cHtU ;v@u"}${;IPB4+K79 .T?yw^{{6Iec%r"s)dH)2NT! N CA̯l|/aҥ @:1DÜf"~󅇾 @<SM]E6±=+oS`0=HB$hHH )Ri;MxF=s9O~tuuerWͥiUȩu̺btkѸ馛}3\ym mgڜU7ЗCzWҦȻyrn lѱd2Adt_~x-W~hr\$+Wʕ+\sȑyXiN/#>/p-llaW"q󔕕[oSܱv#Sg]1H&ӄáXn]?=զi^{?oy@x቉Ŧ#H8J)|qͽ"e-+icdÆ O<ĞHZ&P3%$aO|:::HR]aYeF? %CO )l ZF閎ѭ↕y,H`P!%U|KQYYY*|>gѣ$b"p]ו٬X֭[ԕ'NX`0)RLMDeLm`455 TTT`9B!0x)Blۦ}50/*fΜ^kk3 C3 C)r]W !Rl3nq|]|>M=>52(IO@I"؅Xh|4% L-1 }Tʒ+Js J %KrI.pZHnIENDB`icons/hicolor/48x48/status/000077500000000000000000000000001304552132100157465ustar00rootroot00000000000000icons/hicolor/48x48/status/Makefile.am000066400000000000000000000001721304552132100200020ustar00rootroot00000000000000icondir = $(datadir)/gobby-0.5/icons/hicolor/48x48/status icon_DATA = user-color-indicator.png EXTRA_DIST = $(icon_DATA) icons/hicolor/48x48/status/user-color-indicator.png000066400000000000000000000046311304552132100225240ustar00rootroot00000000000000PNG  IHDR00WsBIT|d pHYs))+ܲtEXtSoftwarewww.inkscape.org< IDATh՚MlE=vdQEX+!  \+.Q. 0r%ₖ\#-r0 8)6CuMWXvTz﷾RJBTIiqX~6Οw\JH  ʾ={d| xh>a9[j?< <fM"Itn_# /= \R~kM*S8<@< unC|?@TLq aHu)5mR,jɻ, 4= /At(RDTDMcX[+׀&ۭlΙNaټβg8 Ǐ̌VKq~k@aDogB&'L۳fSϞ_ 5`^\탱1eV6wT$gbbڜ !^6Cci-ҧVȈ2'W$Q&cnJ7Tl_<+ij0>!*IP^!Np> SS x=Lw]Ja$6:zj &˰*_ua|\+CXGaTZA62>]qhS֜byRu^zIv;7abک=\ iad$`ttԔgT8w Z1Wތ 0?IjqMa8ExE:\~$SfBe}Ӹ,Ir iUysNjW\h0?'a&pm6qyfv-3iGi<8*P>x<F7^v[}m) ŅbPA3UpfVVtFTԶ_ȯsDLl.5 !!QAP$)B)Sk3Mw9$MrCC-(%xVˊ'i5hNLkє>Zl*(R!^W jJL e&djF70URh mΫip6 Sr9IЎaYk@1Ҳ LIY5ز裥o`:HՃa$L 62 4Bm?v pP Xo)N6M5 9gphiz8ӥadʴ2i#ۭiDdKܟ軽ىo$m&~)eG[n]'MmI)lO.hStF- [n睐0VVn2̊r)e i2aXXMd'4Lb,|ٔ;"x:G[/kYATsH_n:? Q)e"xlqW_ؘq"=/n7N67|Q_f鴔r%#izLN.8 k{ewnZڠZڂFέ[sllKJ)]! $q&BlYEtQ(ˋ|9°qaVЂFJP)e}&'/uۼX(V˗_^ˤbPGػw.A"}.[&Nq·૯z›|mVߟaϞLNVoڙ:fy:mVĶA0Q)FG;A Ofڟ=vlBͿ5X8ttoIENDB`icons/hicolor/Makefile.am000066400000000000000000000006571304552132100156700ustar00rootroot00000000000000SUBDIRS = 48x48 scalable gtk_update_icon_cache = gtk-update-icon-cache -f -t $(datadir)/icons/hicolor install-data-hook: update-icon-cache uninstall-hook: update-icon-cache update-icon-cache: @-if test -z "$(DESTDIR)"; then \ echo "Updating Gtk icon cache."; \ $(gtk_update_icon_cache); \ else \ echo "*** Icon cache not updated. After (un)install, run this:"; \ echo "*** $(gtk_update_icon_cache)"; \ fi icons/hicolor/scalable/000077500000000000000000000000001304552132100153725ustar00rootroot00000000000000icons/hicolor/scalable/Makefile.am000066400000000000000000000000361304552132100174250ustar00rootroot00000000000000SUBDIRS = apps actions status icons/hicolor/scalable/actions/000077500000000000000000000000001304552132100170325ustar00rootroot00000000000000icons/hicolor/scalable/actions/Makefile.am000066400000000000000000000002161304552132100210650ustar00rootroot00000000000000icondir = $(datadir)/gobby-0.5/icons/hicolor/scalable/actions icon_DATA = chat.svg document-list.svg user-list.svg EXTRA_DIST = $(icon_DATA) icons/hicolor/scalable/actions/chat.svg000066400000000000000000000165451304552132100205050ustar00rootroot00000000000000 image/svg+xml icons/hicolor/scalable/actions/document-list.svg000066400000000000000000001777171304552132100223660ustar00rootroot00000000000000 image/svg+xml icons/hicolor/scalable/actions/user-list.svg000066400000000000000000000571131304552132100215110ustar00rootroot00000000000000 image/svg+xml image/svg+xml image/svg+xml icons/hicolor/scalable/apps/000077500000000000000000000000001304552132100163355ustar00rootroot00000000000000icons/hicolor/scalable/apps/Makefile.am000066400000000000000000000001461304552132100203720ustar00rootroot00000000000000icondir = $(datadir)/icons/hicolor/scalable/apps icon_DATA = gobby-0.5.svg EXTRA_DIST = $(icon_DATA) icons/hicolor/scalable/apps/gobby-0.5.svg000066400000000000000000001500361304552132100204650ustar00rootroot00000000000000 image/svg+xml image/svg+xml image/svg+xml icons/hicolor/scalable/status/000077500000000000000000000000001304552132100167155ustar00rootroot00000000000000icons/hicolor/scalable/status/Makefile.am000066400000000000000000000001751304552132100207540ustar00rootroot00000000000000icondir = $(datadir)/gobby-0.5/icons/hicolor/scalable/status icon_DATA = user-color-indicator.svg EXTRA_DIST = $(icon_DATA) icons/hicolor/scalable/status/user-color-indicator.svg000066400000000000000000000170051304552132100235050ustar00rootroot00000000000000 image/svg+xml m4/000077500000000000000000000000001304552132100113725ustar00rootroot00000000000000m4/Makefile.am000066400000000000000000000000771304552132100134320ustar00rootroot00000000000000m4macros = \ ac_pkg_check_gtkmm.m4 EXTRA_DIST = $(m4macros) m4/ac_pkg_check_gtkmm.m4000066400000000000000000000003301304552132100154100ustar00rootroot00000000000000AC_DEFUN(PKG_CHECK_GTKMM, [ GTKMM_VER=0 PKG_CHECK_MODULES(GTKMM, gtkmm-2.4 >= 2.8.0, [ GTKMM_VER=28 ], [ PKG_CHECK_MODULES(GTKMM, gtkmm-2.4 >= 2.6.0) GTKMM_VER=26 ]) AC_SUBST(GTKMM_VER) ]) m4/ax_cxx_compile_stdcxx.m4000066400000000000000000000330001304552132100162270ustar00rootroot00000000000000# =========================================================================== # http://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html # =========================================================================== # # SYNOPSIS # # AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional]) # # DESCRIPTION # # Check for baseline language coverage in the compiler for the specified # version of the C++ standard. If necessary, add switches to CXX and # CXXCPP to enable support. VERSION may be '11' (for the C++11 standard) # or '14' (for the C++14 standard). # # The second argument, if specified, indicates whether you insist on an # extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g. # -std=c++11). If neither is specified, you get whatever works, with # preference for an extended mode. # # The third argument, if specified 'mandatory' or if left unspecified, # indicates that baseline support for the specified C++ standard is # required and that the macro should error out if no mode with that # support is found. If specified 'optional', then configuration proceeds # regardless, after defining HAVE_CXX${VERSION} if and only if a # supporting mode is found. # # LICENSE # # Copyright (c) 2008 Benjamin Kosnik # Copyright (c) 2012 Zack Weinberg # Copyright (c) 2013 Roy Stogner # Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov # Copyright (c) 2015 Paul Norman # Copyright (c) 2015 Moritz Klammler # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. #serial 4 dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro dnl (serial version number 13). AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl m4_if([$1], [11], [], [$1], [14], [], [$1], [17], [m4_fatal([support for C++17 not yet implemented in AX_CXX_COMPILE_STDCXX])], [m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl m4_if([$2], [], [], [$2], [ext], [], [$2], [noext], [], [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true], [$3], [mandatory], [ax_cxx_compile_cxx$1_required=true], [$3], [optional], [ax_cxx_compile_cxx$1_required=false], [m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])]) AC_LANG_PUSH([C++])dnl ac_success=no AC_CACHE_CHECK(whether $CXX supports C++$1 features by default, ax_cv_cxx_compile_cxx$1, [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], [ax_cv_cxx_compile_cxx$1=yes], [ax_cv_cxx_compile_cxx$1=no])]) if test x$ax_cv_cxx_compile_cxx$1 = xyes; then ac_success=yes fi m4_if([$2], [noext], [], [dnl if test x$ac_success = xno; then for switch in -std=gnu++$1 -std=gnu++0x; do cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, $cachevar, [ac_save_CXX="$CXX" CXX="$CXX $switch" AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], [eval $cachevar=yes], [eval $cachevar=no]) CXX="$ac_save_CXX"]) if eval test x\$$cachevar = xyes; then CXX="$CXX $switch" if test -n "$CXXCPP" ; then CXXCPP="$CXXCPP $switch" fi ac_success=yes break fi done fi]) m4_if([$2], [ext], [], [dnl if test x$ac_success = xno; then dnl HP's aCC needs +std=c++11 according to: dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf dnl Cray's crayCC needs "-h std=c++11" for switch in -std=c++$1 -std=c++0x +std=c++$1 "-h std=c++$1"; do cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, $cachevar, [ac_save_CXX="$CXX" CXX="$CXX $switch" AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], [eval $cachevar=yes], [eval $cachevar=no]) CXX="$ac_save_CXX"]) if eval test x\$$cachevar = xyes; then CXX="$CXX $switch" if test -n "$CXXCPP" ; then CXXCPP="$CXXCPP $switch" fi ac_success=yes break fi done fi]) AC_LANG_POP([C++]) if test x$ax_cxx_compile_cxx$1_required = xtrue; then if test x$ac_success = xno; then AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.]) fi fi if test x$ac_success = xno; then HAVE_CXX$1=0 AC_MSG_NOTICE([No compiler with C++$1 support was found]) else HAVE_CXX$1=1 AC_DEFINE(HAVE_CXX$1,1, [define if the compiler supports basic C++$1 syntax]) fi AC_SUBST(HAVE_CXX$1) ]) dnl Test body for checking C++11 support m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11], _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 ) dnl Test body for checking C++14 support m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14], _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 ) dnl Tests for new features in C++11 m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[ // If the compiler admits that it is not ready for C++11, why torture it? // Hopefully, this will speed up the test. #ifndef __cplusplus #error "This is not a C++ compiler" #elif __cplusplus < 201103L #error "This is not a C++11 compiler" #else namespace cxx11 { namespace test_static_assert { template struct check { static_assert(sizeof(int) <= sizeof(T), "not big enough"); }; } namespace test_final_override { struct Base { virtual void f() {} }; struct Derived : public Base { virtual void f() override {} }; } namespace test_double_right_angle_brackets { template < typename T > struct check {}; typedef check single_type; typedef check> double_type; typedef check>> triple_type; typedef check>>> quadruple_type; } namespace test_decltype { int f() { int a = 1; decltype(a) b = 2; return a + b; } } namespace test_type_deduction { template < typename T1, typename T2 > struct is_same { static const bool value = false; }; template < typename T > struct is_same { static const bool value = true; }; template < typename T1, typename T2 > auto add(T1 a1, T2 a2) -> decltype(a1 + a2) { return a1 + a2; } int test(const int c, volatile int v) { static_assert(is_same::value == true, ""); static_assert(is_same::value == false, ""); static_assert(is_same::value == false, ""); auto ac = c; auto av = v; auto sumi = ac + av + 'x'; auto sumf = ac + av + 1.0; static_assert(is_same::value == true, ""); static_assert(is_same::value == true, ""); static_assert(is_same::value == true, ""); static_assert(is_same::value == false, ""); static_assert(is_same::value == true, ""); return (sumf > 0.0) ? sumi : add(c, v); } } namespace test_noexcept { int f() { return 0; } int g() noexcept { return 0; } static_assert(noexcept(f()) == false, ""); static_assert(noexcept(g()) == true, ""); } namespace test_constexpr { template < typename CharT > unsigned long constexpr strlen_c_r(const CharT *const s, const unsigned long acc) noexcept { return *s ? strlen_c_r(s + 1, acc + 1) : acc; } template < typename CharT > unsigned long constexpr strlen_c(const CharT *const s) noexcept { return strlen_c_r(s, 0UL); } static_assert(strlen_c("") == 0UL, ""); static_assert(strlen_c("1") == 1UL, ""); static_assert(strlen_c("example") == 7UL, ""); static_assert(strlen_c("another\0example") == 7UL, ""); } namespace test_rvalue_references { template < int N > struct answer { static constexpr int value = N; }; answer<1> f(int&) { return answer<1>(); } answer<2> f(const int&) { return answer<2>(); } answer<3> f(int&&) { return answer<3>(); } void test() { int i = 0; const int c = 0; static_assert(decltype(f(i))::value == 1, ""); static_assert(decltype(f(c))::value == 2, ""); static_assert(decltype(f(0))::value == 3, ""); } } namespace test_uniform_initialization { struct test { static const int zero {}; static const int one {1}; }; static_assert(test::zero == 0, ""); static_assert(test::one == 1, ""); } namespace test_lambdas { void test1() { auto lambda1 = [](){}; auto lambda2 = lambda1; lambda1(); lambda2(); } int test2() { auto a = [](int i, int j){ return i + j; }(1, 2); auto b = []() -> int { return '0'; }(); auto c = [=](){ return a + b; }(); auto d = [&](){ return c; }(); auto e = [a, &b](int x) mutable { const auto identity = [](int y){ return y; }; for (auto i = 0; i < a; ++i) a += b--; return x + identity(a + b); }(0); return a + b + c + d + e; } int test3() { const auto nullary = [](){ return 0; }; const auto unary = [](int x){ return x; }; using nullary_t = decltype(nullary); using unary_t = decltype(unary); const auto higher1st = [](nullary_t f){ return f(); }; const auto higher2nd = [unary](nullary_t f1){ return [unary, f1](unary_t f2){ return f2(unary(f1())); }; }; return higher1st(nullary) + higher2nd(nullary)(unary); } } namespace test_variadic_templates { template struct sum; template struct sum { static constexpr auto value = N0 + sum::value; }; template <> struct sum<> { static constexpr auto value = 0; }; static_assert(sum<>::value == 0, ""); static_assert(sum<1>::value == 1, ""); static_assert(sum<23>::value == 23, ""); static_assert(sum<1, 2>::value == 3, ""); static_assert(sum<5, 5, 11>::value == 21, ""); static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, ""); } // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function // because of this. namespace test_template_alias_sfinae { struct foo {}; template using member = typename T::member_type; template void func(...) {} template void func(member*) {} void test(); void test() { func(0); } } } // namespace cxx11 #endif // __cplusplus >= 201103L ]]) dnl Tests for new features in C++14 m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[ // If the compiler admits that it is not ready for C++14, why torture it? // Hopefully, this will speed up the test. #ifndef __cplusplus #error "This is not a C++ compiler" #elif __cplusplus < 201402L #error "This is not a C++14 compiler" #else namespace cxx14 { namespace test_polymorphic_lambdas { int test() { const auto lambda = [](auto&&... args){ const auto istiny = [](auto x){ return (sizeof(x) == 1UL) ? 1 : 0; }; const int aretiny[] = { istiny(args)... }; return aretiny[0]; }; return lambda(1, 1L, 1.0f, '1'); } } namespace test_binary_literals { constexpr auto ivii = 0b0000000000101010; static_assert(ivii == 42, "wrong value"); } namespace test_generalized_constexpr { template < typename CharT > constexpr unsigned long strlen_c(const CharT *const s) noexcept { auto length = 0UL; for (auto p = s; *p; ++p) ++length; return length; } static_assert(strlen_c("") == 0UL, ""); static_assert(strlen_c("x") == 1UL, ""); static_assert(strlen_c("test") == 4UL, ""); static_assert(strlen_c("another\0test") == 7UL, ""); } namespace test_lambda_init_capture { int test() { auto x = 0; const auto lambda1 = [a = x](int b){ return a + b; }; const auto lambda2 = [a = lambda1(x)](){ return a; }; return lambda2(); } } namespace test_digit_seperators { constexpr auto ten_million = 100'000'000; static_assert(ten_million == 100000000, ""); } namespace test_return_type_deduction { auto f(int& x) { return x; } decltype(auto) g(int& x) { return x; } template < typename T1, typename T2 > struct is_same { static constexpr auto value = false; }; template < typename T > struct is_same { static constexpr auto value = true; }; int test() { auto x = 0; static_assert(is_same::value, ""); static_assert(is_same::value, ""); return x; } } } // namespace cxx14 #endif // __cplusplus >= 201402L ]]) m4/ax_cxx_compile_stdcxx_11.m4000066400000000000000000000032121304552132100165320ustar00rootroot00000000000000# ============================================================================ # http://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx_11.html # ============================================================================ # # SYNOPSIS # # AX_CXX_COMPILE_STDCXX_11([ext|noext], [mandatory|optional]) # # DESCRIPTION # # Check for baseline language coverage in the compiler for the C++11 # standard; if necessary, add switches to CXX and CXXCPP to enable # support. # # This macro is a convenience alias for calling the AX_CXX_COMPILE_STDCXX # macro with the version set to C++11. The two optional arguments are # forwarded literally as the second and third argument respectively. # Please see the documentation for the AX_CXX_COMPILE_STDCXX macro for # more information. If you want to use this macro, you also need to # download the ax_cxx_compile_stdcxx.m4 file. # # LICENSE # # Copyright (c) 2008 Benjamin Kosnik # Copyright (c) 2012 Zack Weinberg # Copyright (c) 2013 Roy Stogner # Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov # Copyright (c) 2015 Paul Norman # Copyright (c) 2015 Moritz Klammler # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. #serial 17 AX_REQUIRE_DEFINED([AX_CXX_COMPILE_STDCXX]) AC_DEFUN([AX_CXX_COMPILE_STDCXX_11], [AX_CXX_COMPILE_STDCXX([11], [$1], [$2])]) po/000077500000000000000000000000001304552132100114705ustar00rootroot00000000000000po/.gitignore000066400000000000000000000001111304552132100134510ustar00rootroot00000000000000POTFILES Makefile.in Makefile.in.in stamp-it .intltool-merge-cache *.gmo po/ChangeLog000066400000000000000000000045111304552132100132430ustar00rootroot000000000000002016-10-28 Pietro Brenna * it_IT.po: Add italian translation. * LINGUAS: Add it_IT. 2011-12-01 Wei-Lun Chao * zh_TW.po: Add traditional Chinese translation. * LINGUAS: Add zh_TW. 2010-11-18 Takahiro Sunaga * ja.po: Add Japanese translation. * LINGUAS: Add ja. 2010-11-16 Claude Paroz * fr.po: Add French translation. * LINGUAS: Add fr. 2010-06-20 Armin Burgmeier * en_GB.po: * de.po: Brought up to date. 2010-06-20 Γιάννης Κασκαμανίδης * LINGUAS: Added el_GR. * el_GR.po: Added greek translation. 2010-02-26 Armin Burgmeier * de.po: Choose v as mnemonic for "Direktverbindung" as d is already in use by "_Datei" in the menu bar. 2010-02-14 Benjamin Herr * en_GB.po: * de.po: Brought up to date 2010-02-14 Armin Burgmeier , Benjamin Herr , Michael Frey * de.po: Added german translation. * LINGUAS: Added de. 2009-12-19 Armin Burgmeier * gobby05.pot: Added .pot file, to allow synchronization with launchpad. * Makefile.in.in: Include gobby05.pot in distribution tarball. 2009-12-19 Armin Burgmeier * en_GB.po: Update. 2009-12-16 Armin Burgmeier * POTFILES.in: Updated. 2009-12-03 Gabríel A. Pétursson * LINGUAS: * en_GB.po: Added British English translation. 2009-11-15 Armin Burgmeier * POTFILES.in: Updated. * LINGUAS: Removed all languages. * *.po: Removed as these were just remains from Gobby 0.4.x which contained only very few strings that are still valid, and a lot of invalid fuzzy matches. 2009-08-29 Armin Burgmeier * POTFILES.in: Updated. 2009-04-24 Armin Burgmeier * POTFILES.in: Updated. 2008-11-23 Armin Burgmeier * POTFILES.in: Added contrib/gobby-0.5.desktop.in. * gobby05.pot: Regenerated. * *.po: Updated for the new strings. 2008-10-16 Armin Burgmeier * POTFILES.in: * gobby.pot: Updated for 0.4.90 release. 2008-09-19 Armin Burgmeier * POTFILES.in: Regenerated. * gobby.pot: * *.po: Updated to the strings of 0.4.90. po/LINGUAS000066400000000000000000000000531304552132100125130ustar00rootroot00000000000000ca de el_GR en_GB fr ja zh_TW pt_BR it_IT po/POTFILES.in000066400000000000000000000030461304552132100132500ustar00rootroot00000000000000code/application.cpp code/commands/auth-commands.cpp code/commands/browser-commands.cpp code/commands/browser-context-commands.cpp code/commands/file-commands.cpp code/commands/file-tasks/task-export-html.cpp code/commands/file-tasks/task-new.cpp code/commands/file-tasks/task-open.cpp code/commands/file-tasks/task-open-file.cpp code/commands/file-tasks/task-open-location.cpp code/commands/file-tasks/task-save.cpp code/commands/help-commands.cpp code/commands/subscription-commands.cpp code/commands/synchronization-commands.cpp code/commands/user-join-commands.cpp code/core/browser.cpp code/core/certificatemanager.cpp code/core/filechooser.cpp code/core/foldermanager.cpp code/core/huebutton.cpp code/core/selfhoster.cpp code/core/sessionuserview.cpp code/core/statusbar.cpp code/core/textsessionview.cpp code/core/userlist.cpp code/dialogs/connection-dialog.cpp code/dialogs/document-location-dialog.cpp code/dialogs/find-dialog.cpp code/dialogs/goto-dialog.cpp code/dialogs/initial-dialog.cpp code/dialogs/open-location-dialog.cpp code/dialogs/password-dialog.cpp code/dialogs/preferences-dialog.cpp code/gobby-resources.c code/operations/operation-delete.cpp code/operations/operation-export-html.cpp code/operations/operation-new.cpp code/operations/operation-open.cpp code/operations/operation-open-multiple.cpp code/operations/operation-save.cpp code/operations/operation-subscribe-path.cpp code/resources/ui/menu.ui.h code/resources/ui/toolbar.ui.h code/util/file.cpp code/util/i18n.cpp code/util/i18n.hpp code/util/uri.cpp code/window.cpp gobby-0.5.desktop.in po/ca.po000066400000000000000000001304711304552132100124210ustar00rootroot00000000000000# Catalan translation of Gobby. # Copyright © 2012 Free Software Foundation, Inc. # This file is distributed under the same license as the gobby package. # Jordi Mallach , 2012. # msgid "" msgstr "" "Project-Id-Version: gobby 0.4.94\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2014-08-23 13:43-0400\n" "PO-Revision-Date: 2012-12-04 18:12+0100\n" "Last-Translator: Jordi Mallach \n" "Language-Team: Catalan \n" "Language: ca\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n!=1;\n" #: ../code/commands/auth-commands.cpp:196 #, fuzzy msgid "Password required" msgstr "Es requereix una contrasenya per a la sessió" #: ../code/commands/auth-commands.cpp:214 #, fuzzy msgid "Incorrect password" msgstr "Estableix una contrasenya…" #: ../code/commands/browser-commands.cpp:288 msgid "Subscribing to %1..." msgstr "" #: ../code/commands/browser-commands.cpp:301 msgid "Subscribing to chat on %1..." msgstr "" #: ../code/commands/browser-commands.cpp:591 msgid "Subscription failed" msgstr "" #: ../code/commands/browser-context-commands.cpp:86 msgid "Could not find a location where to store the certificate" msgstr "" #: ../code/commands/browser-context-commands.cpp:153 msgid "_Remove" msgstr "" #: ../code/commands/browser-context-commands.cpp:188 msgid "_Disconnect from Server" msgstr "" #: ../code/commands/browser-context-commands.cpp:220 #, fuzzy msgid "Create _Account..." msgstr "Crea un document…" #: ../code/commands/browser-context-commands.cpp:245 #, fuzzy msgid "Create Do_cument..." msgstr "Crea un document…" #: ../code/commands/browser-context-commands.cpp:280 #, fuzzy msgid "Create Di_rectory..." msgstr "Crea una sessió…" #: ../code/commands/browser-context-commands.cpp:293 #, fuzzy msgid "_Open Document..." msgstr "Obre un document…" #: ../code/commands/browser-context-commands.cpp:312 #, fuzzy msgid "_Permissions..." msgstr "_Sessió" #: ../code/commands/browser-context-commands.cpp:324 msgid "D_elete" msgstr "" #: ../code/commands/browser-context-commands.cpp:405 msgid "Choose a name for the directory" msgstr "" #: ../code/commands/browser-context-commands.cpp:406 #, fuzzy msgid "Choose a name for the document" msgstr "Tanca un document obert" #: ../code/commands/browser-context-commands.cpp:407 msgid "_Directory Name:" msgstr "" #: ../code/commands/browser-context-commands.cpp:408 #, fuzzy msgid "_Document Name:" msgstr "Gestió de documents" #: ../code/commands/browser-context-commands.cpp:411 msgid "C_reate" msgstr "" #: ../code/commands/browser-context-commands.cpp:415 #, fuzzy msgid "New Directory" msgstr "Direcció" #: ../code/commands/browser-context-commands.cpp:416 #: ../code/commands/file-tasks/task-new.cpp:36 #, fuzzy msgid "New Document" msgstr "Desa el document" #: ../code/commands/browser-context-commands.cpp:436 #: ../code/commands/file-tasks/task-open-file.cpp:22 msgid "Choose a text file to open" msgstr "" #: ../code/commands/browser-context-commands.cpp:558 #: ../code/commands/browser-context-commands.cpp:569 msgid "Account successfully created" msgstr "" #: ../code/commands/browser-context-commands.cpp:562 msgid "When re-connecting to the server, the new account will be used." msgstr "" #: ../code/commands/browser-context-commands.cpp:573 msgid "" "The certificate has been stored at %1.\n" "\n" "To login to this account, set the certificate in Gobby's preferences and re-" "connect to the server." msgstr "" #: ../code/commands/browser-context-commands.cpp:586 msgid "Failed to create account" msgstr "" #: ../code/commands/browser-context-commands.cpp:589 msgid "Could not save the certificate for the account: %1" msgstr "" #: ../code/commands/file-tasks/task-export-html.cpp:27 msgid "Choose a location to export document \"%1\" to" msgstr "" #: ../code/commands/file-tasks/task-open.cpp:46 msgid "Querying \"%1\"..." msgstr "" #: ../code/commands/file-tasks/task-open.cpp:103 #: ../code/operations/operation-open.cpp:474 #: ../code/operations/operation-open-multiple.cpp:180 #, fuzzy msgid "Failed to open document \"%1\"" msgstr "Tanca un document obert" #: ../code/commands/file-tasks/task-save.cpp:24 msgid "Choose a location to save document \"%1\" to" msgstr "" #. Help browser could not be invoked, show an error message to the user. #: ../code/commands/help-commands.cpp:70 msgid "There was an error displaying help." msgstr "" #: ../code/commands/help-commands.cpp:93 msgid "Contributors:" msgstr "" #: ../code/commands/help-commands.cpp:99 msgid "British English:" msgstr "" #: ../code/commands/help-commands.cpp:101 msgid "German:" msgstr "" #: ../code/commands/help-commands.cpp:118 msgid "" "Permission to use, copy, modify, and/or distribute this software for any " "urpose with or without fee is hereby granted, provided that the above " "copyright notice and this permission notice appear in all copies.\n" "\n" "THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES " "WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF " "MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY " "SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES " "WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION " "OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN " "CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE." msgstr "" #: ../code/commands/subscription-commands.cpp:148 msgid "" "The connection to the publisher of this document has been lost. Further " "changes to the document could not be synchronized to others anymore, " "therefore the document cannot be edited anymore.\n" "\n" "Please note also that it is possible that not all of your latest changes " "have reached the publisher before the connection was lost." msgstr "" #: ../code/commands/synchronization-commands.cpp:30 msgid "Synchronization in progress... %1%%" msgstr "" #. Document cannot be used if an error happened #. during synchronization. #: ../code/commands/synchronization-commands.cpp:42 msgid "This document cannot be used." msgstr "" #: ../code/commands/synchronization-commands.cpp:45 #: ../code/commands/user-join-commands.cpp:43 msgid "" "If you have an idea what could have caused the problem, then you may attempt " "to solve it and try again (after having closed this document). Otherwise it " "is most likely a bug in the software. In that case, please file a bug report " "at http://gobby.0x539.de/trac/newticket and provide as much information as " "you can, including what you did when the problem occurred and how to " "reproduce the problem (if possible) so that we can fix the problem in a " "later version. Thank you." msgstr "" #: ../code/commands/synchronization-commands.cpp:59 msgid "Synchronization failed: %1" msgstr "" #. TODO: Adjust this for chat sessions #: ../code/commands/user-join-commands.cpp:38 msgid "" "You can still watch others editing the document, but you cannot edit it " "yourself." msgstr "" #: ../code/commands/user-join-commands.cpp:57 msgid "User Join failed: %1" msgstr "" #: ../code/commands/user-join-commands.cpp:64 msgid "Permissions are not granted to modify the document." msgstr "" #: ../code/core/browser.cpp:111 #, fuzzy msgid "_Direct Connection" msgstr "Direcció" #: ../code/core/browser.cpp:113 #, fuzzy msgid "Host Name:" msgstr "Nom:" #: ../code/core/certificatemanager.cpp:83 #, c-format msgid "Failed to write Diffie-Hellman parameters to \"%s\": %s" msgstr "" #: ../code/core/certificatemanager.cpp:276 #, c-format msgid "Failed to read Diffie-Hellman parameters: %s" msgstr "" #: ../code/core/certificatemanager.cpp:338 msgid "File does not contain a X.509 certificate" msgstr "" #: ../code/core/certificatemanager.cpp:414 msgid "Certificate does not belong to the chosen key" msgstr "" #: ../code/core/foldermanager.cpp:322 msgid "The document has been removed from the server." msgstr "" #: ../code/core/header.cpp:203 #, fuzzy msgid "_None" msgstr "Cap" #: ../code/core/header.cpp:227 #, fuzzy msgid "_File" msgstr "Fitxers" #: ../code/core/header.cpp:230 msgid "_Open..." msgstr "" #: ../code/core/header.cpp:233 #, fuzzy msgid "Open _Location..." msgstr "Obre un document…" #: ../code/core/header.cpp:237 #, fuzzy msgid "Save _As..." msgstr "Desa la sessió…" #: ../code/core/header.cpp:241 ../code/core/iconmanager.cpp:43 msgid "Save All" msgstr "" #: ../code/core/header.cpp:241 msgid "Save all open files locally" msgstr "" #: ../code/core/header.cpp:243 msgid "Export As _HTML..." msgstr "" #: ../code/core/header.cpp:246 msgid "Connect _to Server..." msgstr "" #: ../code/core/header.cpp:251 msgid "_Edit" msgstr "_Edita" #: ../code/core/header.cpp:259 #, fuzzy msgid "_Find..." msgstr "Cerca…" #: ../code/core/header.cpp:261 #, fuzzy msgid "Find Ne_xt" msgstr "Cerca:" #: ../code/core/header.cpp:262 msgid "Find next match of phrase searched for" msgstr "" #: ../code/core/header.cpp:265 msgid "Find Pre_vious" msgstr "" #: ../code/core/header.cpp:266 msgid "Find previous match of phrase searched for" msgstr "" #: ../code/core/header.cpp:271 #, fuzzy msgid "Find and Rep_lace..." msgstr "Cerca i reemplaça…" #: ../code/core/header.cpp:275 #, fuzzy msgid "Go to _Line..." msgstr "Vés a la línia…" #: ../code/core/header.cpp:279 #, fuzzy msgid "Pr_eferences..." msgstr "Preferències…" #: ../code/core/header.cpp:281 #, fuzzy msgid "_View" msgstr "Visualitza" #: ../code/core/header.cpp:284 msgid "Reset User Colors" msgstr "" #: ../code/core/header.cpp:285 #, fuzzy msgid "Hide user colors in current document" msgstr "Cerca un text al document actual" #: ../code/core/header.cpp:289 msgid "Fullscreen" msgstr "" #: ../code/core/header.cpp:290 msgid "Show the editing window in fullscreen mode" msgstr "" #: ../code/core/header.cpp:294 msgid "Zoom In" msgstr "" #: ../code/core/header.cpp:297 msgid "Zoom Out" msgstr "" #: ../code/core/header.cpp:300 #, fuzzy msgid "View Toolbar" msgstr "Barra d'eines" #: ../code/core/header.cpp:301 msgid "Whether to show the toolbar" msgstr "" #: ../code/core/header.cpp:305 msgid "View Statusbar" msgstr "" #: ../code/core/header.cpp:306 msgid "Whether to show the statusbar" msgstr "" #: ../code/core/header.cpp:310 msgid "View Document Browser" msgstr "" #: ../code/core/header.cpp:311 msgid "Whether to show the document browser" msgstr "" #: ../code/core/header.cpp:315 #, fuzzy msgid "View Chat" msgstr "Xat" #: ../code/core/header.cpp:316 msgid "Whether to show the chat pane" msgstr "" #: ../code/core/header.cpp:320 #, fuzzy msgid "View Document User List" msgstr "Llista de documents" #: ../code/core/header.cpp:321 msgid "Whether to show the user list for documents" msgstr "" #: ../code/core/header.cpp:325 msgid "View Chat User List" msgstr "" #: ../code/core/header.cpp:326 msgid "Whether to show the user list for the chat" msgstr "" #: ../code/core/header.cpp:330 msgid "_Highlight Mode" msgstr "" #: ../code/core/header.cpp:336 msgid "_Help" msgstr "A_juda" #: ../code/core/header.cpp:339 msgid "_Contents" msgstr "" #: ../code/core/header.cpp:340 #, fuzzy msgid "Opens the Gobby manual" msgstr "Obre una sessió d'obby nova" #: ../code/core/header.cpp:343 #, fuzzy msgid "_About..." msgstr "Quant a" #: ../code/core/header.cpp:344 msgid "Shows Gobby's copyright and credits" msgstr "Mostra la informació de copyright i autoria del Gobby" #: ../code/core/iconmanager.cpp:49 msgid "User list" msgstr "Llista d'usuaris" #: ../code/core/iconmanager.cpp:55 msgid "Document list" msgstr "Llista de documents" #: ../code/core/iconmanager.cpp:61 ../code/window.cpp:52 msgid "Chat" msgstr "Xat" #: ../code/core/iconmanager.cpp:68 msgid "User Color Indicator" msgstr "" #: ../code/core/selfhoster.cpp:103 msgid "Generating 2048-bit Diffie-Hellman parameters..." msgstr "" #: ../code/core/selfhoster.cpp:135 msgid "Failed to generate Diffie-Hellman parameters" msgstr "" #: ../code/core/selfhoster.cpp:137 msgid "" "This means that Perfect Forward Secrecy (PFS) is not available. Restart " "Gobby to re-try generating the parameters. The specific error was:\n" "\n" "%1" msgstr "" #: ../code/core/selfhoster.cpp:262 msgid "" "In order to start sharing your documents, choose a private key and " "certificate or create a new pair in the preferences" msgstr "" #: ../code/core/selfhoster.cpp:282 #, fuzzy msgid "Failed to share documents" msgstr "Desa el document «%0%»" #: ../code/core/sessionuserview.cpp:39 #, fuzzy msgid "User List" msgstr "Llista d'usuaris" #: ../code/core/statusbar.cpp:418 #, fuzzy msgid "Ln %1, Col %2\t%3" msgstr "Línia: %0%, Columna: %1%" #: ../code/core/statusbar.cpp:421 msgid "OVR" msgstr "" #: ../code/core/statusbar.cpp:421 msgid "INS" msgstr "" #: ../code/core/textsessionview.cpp:638 msgid "Text written by %1" msgstr "" #: ../code/core/textsessionview.cpp:644 msgid "Unowned text" msgstr "" #: ../code/core/userlist.cpp:108 #, fuzzy msgid "Users" msgstr "Usuari" #: ../code/dialogs/connection-dialog.cpp:26 msgid "Connect to Server" msgstr "" #: ../code/dialogs/connection-dialog.cpp:30 msgid "Please enter a host name with which to establish a connection." msgstr "" #: ../code/dialogs/connection-dialog.cpp:32 msgid "_Remote Endpoint:" msgstr "" #: ../code/dialogs/document-location-dialog.cpp:28 #, fuzzy msgid "Document Name:" msgstr "Gestió de documents" #: ../code/dialogs/document-location-dialog.cpp:155 #, fuzzy msgid "Select document's target location" msgstr "Desa el document en una altra ubicació" #: ../code/dialogs/document-location-dialog.cpp:157 msgid "Choose a directory to create the document into:" msgstr "" #: ../code/dialogs/document-location-dialog.cpp:163 #, fuzzy msgid "Select documents' target location" msgstr "Desa el document en una altra ubicació" #: ../code/dialogs/document-location-dialog.cpp:165 msgid "Choose a directory to create the documents into:" msgstr "" #: ../code/dialogs/find-dialog.cpp:44 ../code/dialogs/find-dialog.cpp:142 #, fuzzy msgid "Find" msgstr "Cerca…" #: ../code/dialogs/find-dialog.cpp:47 #, fuzzy msgid "_Search for:" msgstr "Cerca" #: ../code/dialogs/find-dialog.cpp:49 #, fuzzy msgid "Replace _with:" msgstr "Substitueix amb:" #: ../code/dialogs/find-dialog.cpp:51 #, fuzzy msgid "_Match case" msgstr "Distingeix entre majúscules i minúscules" #: ../code/dialogs/find-dialog.cpp:52 #, fuzzy msgid "Match _entire word only" msgstr "Només paraules senceres" #: ../code/dialogs/find-dialog.cpp:53 msgid "Search _backwards" msgstr "" #: ../code/dialogs/find-dialog.cpp:54 msgid "Wra_p around" msgstr "" #: ../code/dialogs/find-dialog.cpp:100 #, fuzzy msgid "Replace _All" msgstr "Reemplaça-ho _tot" #: ../code/dialogs/find-dialog.cpp:102 msgid "_Replace" msgstr "_Reemplaça" #: ../code/dialogs/find-dialog.cpp:142 #, fuzzy msgid "Replace" msgstr "_Reemplaça" #: ../code/dialogs/find-dialog.cpp:161 ../code/dialogs/find-dialog.cpp:177 #, fuzzy msgid "Phrase \"%1\" has not been found" msgstr "No s'ha trobat «%0%» en el document." #: ../code/dialogs/find-dialog.cpp:335 #, fuzzy msgid "No occurrence has been replaced" msgstr "No s'ha reemplaçat cap coincidència" #: ../code/dialogs/find-dialog.cpp:341 #, fuzzy msgid "%1 occurrence has been replaced" msgid_plural "%1 occurrences have been replaced" msgstr[0] "S'ha reemplaçat %0% coincidència" msgstr[1] "S'han reemplaçat %0% coincidències" #: ../code/dialogs/goto-dialog.cpp:26 msgid "Go to line" msgstr "Vés a la línia" #: ../code/dialogs/goto-dialog.cpp:29 #, fuzzy msgid "Line _number:" msgstr "Número de línia:" #: ../code/dialogs/goto-dialog.cpp:50 msgid "Go To _Line" msgstr "" #: ../code/dialogs/initial-dialog.cpp:76 msgid "Generating 2048-bit RSA private key..." msgstr "" #: ../code/dialogs/initial-dialog.cpp:99 msgid "Failed to generate private key" msgstr "" #: ../code/dialogs/initial-dialog.cpp:102 msgid "" "%1\n" "\n" "You can try again to create a key in the Security tab of the preferences " "dialog." msgstr "" #: ../code/dialogs/initial-dialog.cpp:141 msgid "Failed to generate self-signed certificate" msgstr "" #: ../code/dialogs/initial-dialog.cpp:144 msgid "" "%1\n" "\n" "You can try again to create a certificate in the Security tab of the " "preferences dialog." msgstr "" #: ../code/dialogs/initial-dialog.cpp:198 msgid "Choose a user color" msgstr "" #: ../code/dialogs/initial-dialog.cpp:204 msgid "Welcome to Gobby" msgstr "" #: ../code/dialogs/initial-dialog.cpp:216 msgid "" "Before we start, a few options need to be configured. You can later change " "them by choosing Edit ▸ Preferences from the menu." msgstr "" #: ../code/dialogs/initial-dialog.cpp:224 #, fuzzy msgid "User Name" msgstr "Usuari" #: ../code/dialogs/initial-dialog.cpp:226 msgid "Your name as shown to other users." msgstr "" #: ../code/dialogs/initial-dialog.cpp:235 #, fuzzy msgid "User Color" msgstr "Llista d'usuaris" #: ../code/dialogs/initial-dialog.cpp:237 msgid "The color with which text you have written is branded." msgstr "" #: ../code/dialogs/initial-dialog.cpp:256 #, fuzzy msgid "Remote Connections" msgstr "S'ha perdut la connexió" #: ../code/dialogs/initial-dialog.cpp:262 #: ../code/dialogs/preferences-dialog.cpp:217 msgid "Allow remote users to edit local documents" msgstr "" #: ../code/dialogs/initial-dialog.cpp:268 #, fuzzy msgid "Ask remote users for a password" msgstr "Estableix la contrasenya d'usuari" #: ../code/dialogs/initial-dialog.cpp:273 #: ../code/dialogs/preferences-dialog.cpp:220 msgid "Password:" msgstr "Contrasenya:" #: ../code/dialogs/initial-dialog.cpp:297 msgid "No authentication (Not recommended)" msgstr "" #: ../code/dialogs/initial-dialog.cpp:304 msgid "" "Don't authenticate ourselves to remote users. Data transfer will be " "unencrypted." msgstr "" #: ../code/dialogs/initial-dialog.cpp:313 msgid "Create a self-signed certificate (Recommended)" msgstr "" #: ../code/dialogs/initial-dialog.cpp:321 msgid "" "It may take a minute or two until remote users can connect while the " "security certificate is being generated." msgstr "" #: ../code/dialogs/initial-dialog.cpp:331 msgid "Use an existing certificate (Expert option)" msgstr "" #: ../code/dialogs/initial-dialog.cpp:338 msgid "" "Use an existing private key and certificate for authentication. The files " "must be in PEM format." msgstr "" #: ../code/dialogs/initial-dialog.cpp:347 #: ../code/dialogs/preferences-dialog.cpp:766 msgid "Private key:" msgstr "" #: ../code/dialogs/initial-dialog.cpp:349 #: ../code/dialogs/preferences-dialog.cpp:772 msgid "Certificate:" msgstr "" #: ../code/dialogs/open-location-dialog.cpp:26 msgid "Open Location" msgstr "" #: ../code/dialogs/open-location-dialog.cpp:27 msgid "Enter the _location (URI) of the file you would like to open:" msgstr "" #: ../code/dialogs/password-dialog.cpp:25 #, fuzzy msgid "Password Required" msgstr "Es requereix una contrasenya per a la sessió" #: ../code/dialogs/password-dialog.cpp:31 msgid "Connection to host \"%1\" requires a password." msgstr "" #: ../code/dialogs/password-dialog.cpp:32 msgid "Invalid password for host \"%1\". Please try again." msgstr "" #: ../code/dialogs/password-dialog.cpp:35 #, fuzzy msgid "Server _Password:" msgstr "Contrasenya d'usuari:" #: ../code/dialogs/preferences-dialog.cpp:198 msgid "Settings" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:199 msgid "Remote Users" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:200 #, fuzzy msgid "Local Documents" msgstr "Desa tots els documents" #: ../code/dialogs/preferences-dialog.cpp:202 #, fuzzy msgid "User name:" msgstr "Nom nou:" #: ../code/dialogs/preferences-dialog.cpp:204 #, fuzzy msgid "User color:" msgstr "Contrasenya d'usuari:" #: ../code/dialogs/preferences-dialog.cpp:206 msgid "Color intensity:" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:208 msgid "Choose a new user color" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:209 msgid "Show cursors of remote users" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:210 msgid "Show selections of remote users" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:212 #, fuzzy msgid "Highlight current line of remote users" msgstr "Realça la línia actual" #: ../code/dialogs/preferences-dialog.cpp:214 msgid "Indicate cursor position of remote users in the scrollbar" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:219 msgid "Require remote users to enter a password" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:222 msgid "Port:" msgstr "Port:" #: ../code/dialogs/preferences-dialog.cpp:226 msgid "Remember local documents after Gobby restart" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:227 msgid "Local documents directory:" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:423 msgid "Tab Stops" msgstr "Tabulacions" #: ../code/dialogs/preferences-dialog.cpp:424 msgid "Indentation" msgstr "Sagnat" #: ../code/dialogs/preferences-dialog.cpp:425 #, fuzzy msgid "Home/End Behavior" msgstr "Comportament d'Inici/Fi" #: ../code/dialogs/preferences-dialog.cpp:426 msgid "File Saving" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:427 #, fuzzy msgid "_Tab width:" msgstr "Amplada del tabulador:" #: ../code/dialogs/preferences-dialog.cpp:429 #, fuzzy msgid "Insert _spaces instead of tabs" msgstr "Insereix espais en comptes de tabulacions" #: ../code/dialogs/preferences-dialog.cpp:430 #, fuzzy msgid "Enable automatic _indentation" msgstr "Habilita el sagnat automàtic" #: ../code/dialogs/preferences-dialog.cpp:431 #, fuzzy msgid "Smart _home/end" msgstr "Inici/fi intel·ligent" #: ../code/dialogs/preferences-dialog.cpp:432 #, fuzzy msgid "Enable _automatic saving of documents" msgstr "Habilita el sagnat automàtic" #: ../code/dialogs/preferences-dialog.cpp:434 msgid "Autosave interval in _minutes:" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:455 msgid "" "With this option enabled, Home/End keys move to first/last character before " "going to the start/end of the line." msgstr "" "Amb aquesta opció habilitada, les tecles Inici/Fi mouen al primer/darrer " "caràcter abans d'anar al principi/final de la línia." #: ../code/dialogs/preferences-dialog.cpp:525 #, fuzzy msgid "Text Wrapping" msgstr "Ajustament del text" #: ../code/dialogs/preferences-dialog.cpp:526 #, fuzzy msgid "Line Numbers" msgstr "Números de línies" #: ../code/dialogs/preferences-dialog.cpp:527 #, fuzzy msgid "Current Line" msgstr "Línia actual" #: ../code/dialogs/preferences-dialog.cpp:528 #, fuzzy msgid "Right Margin" msgstr "Marge dret" #: ../code/dialogs/preferences-dialog.cpp:529 #, fuzzy msgid "Bracket Matching" msgstr "Emparellament de claus" #: ../code/dialogs/preferences-dialog.cpp:530 msgid "Whitespace Display" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:531 msgid "Enable text wrapping" msgstr "Habilita l'ajustament automàtic del text" #: ../code/dialogs/preferences-dialog.cpp:532 msgid "Do not split words over two lines" msgstr "No divideixes paraules en dos línies" #: ../code/dialogs/preferences-dialog.cpp:533 msgid "Display line numbers" msgstr "Mostra els números de línia" #: ../code/dialogs/preferences-dialog.cpp:534 msgid "Highlight current line" msgstr "Realça la línia actual" #: ../code/dialogs/preferences-dialog.cpp:535 msgid "Display right margin" msgstr "Mostra el marge dret" #: ../code/dialogs/preferences-dialog.cpp:536 msgid "Right margin at column:" msgstr "Marge dreta a la columna:" #: ../code/dialogs/preferences-dialog.cpp:537 msgid "Highlight matching bracket" msgstr "Realça la clau emparellada" #: ../code/dialogs/preferences-dialog.cpp:589 msgid "Display no whitespace" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:592 msgid "Display spaces" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:596 #, fuzzy msgid "Display tabs" msgstr "Mostra els números de línia" #: ../code/dialogs/preferences-dialog.cpp:600 msgid "Display tabs and spaces" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:651 msgid "Toolbar" msgstr "Barra d'eines" #: ../code/dialogs/preferences-dialog.cpp:652 msgid "Font" msgstr "Tipus de lletra" #: ../code/dialogs/preferences-dialog.cpp:653 msgid "Color Scheme" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:661 msgid "Show text only" msgstr "Mostra només text" #: ../code/dialogs/preferences-dialog.cpp:663 msgid "Show icons only" msgstr "Mostra només icones" #: ../code/dialogs/preferences-dialog.cpp:665 msgid "Show both icons and text" msgstr "Mostra icones i text" #: ../code/dialogs/preferences-dialog.cpp:667 msgid "Show text besides icons" msgstr "Mostra text al costat de les icones" #: ../code/dialogs/preferences-dialog.cpp:756 msgid "Trusted CAs" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:757 #, fuzzy msgid "Secure Connection" msgstr "S'ha perdut la connexió" #: ../code/dialogs/preferences-dialog.cpp:758 #, fuzzy msgid "Authentication" msgstr "Sagnat" #: ../code/dialogs/preferences-dialog.cpp:759 msgid "Select a file containing trusted CAs" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:764 msgid "None" msgstr "Cap" #: ../code/dialogs/preferences-dialog.cpp:765 msgid "Use a certificate" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:767 msgid "Select a private key file" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:769 #: ../code/dialogs/preferences-dialog.cpp:776 #, fuzzy msgid "Create New..." msgstr "Crea una sessió…" #: ../code/dialogs/preferences-dialog.cpp:773 msgid "Select a certificate file" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:801 msgid "Use TLS if possible" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:804 msgid "Always use TLS" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:892 msgid "Error reading file:" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:916 msgid "2048-bit RSA private key is being generated, please wait..." msgstr "" #: ../code/dialogs/preferences-dialog.cpp:948 msgid "Select a location for the generated key" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:966 msgid "Select a location for the generated certificate" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:1068 msgid "Preferences" msgstr "Preferències" #: ../code/dialogs/preferences-dialog.cpp:1073 msgid "User" msgstr "Usuari" #: ../code/dialogs/preferences-dialog.cpp:1074 msgid "Editor" msgstr "Editor" #: ../code/dialogs/preferences-dialog.cpp:1075 msgid "View" msgstr "Visualitza" #: ../code/dialogs/preferences-dialog.cpp:1076 msgid "Appearance" msgstr "Aparença" #: ../code/dialogs/preferences-dialog.cpp:1077 msgid "Security" msgstr "" #: ../code/main.cpp:194 msgid "Gobby options" msgstr "" #: ../code/main.cpp:194 msgid "Options related to Gobby" msgstr "" #: ../code/main.cpp:199 msgid "Display version information and exit" msgstr "" #: ../code/main.cpp:206 msgid "Also start a new Gobby instance when there is one running already" msgstr "" #: ../code/main.cpp:214 msgid "Connect to given host on startup, can be given multiple times" msgstr "" #: ../code/main.cpp:215 msgid "HOSTNAME" msgstr "" #: ../code/operations/operation-delete.cpp:65 msgid "Removing node \"%1\"..." msgstr "" #: ../code/operations/operation-delete.cpp:75 msgid "Failed to delete node \"%1\"" msgstr "" #: ../code/operations/operation-export-html.cpp:168 #, c-format msgid "written by: %s" msgstr "" #: ../code/operations/operation-export-html.cpp:241 msgid "" msgstr "" #. %1$s is session name/hostname #. %2$s is path within the session #. %3$s is current date as formatted by %c, #. %4$s is a link to the gobby site, it must be present because #. we need to handle that manually to insert a hyperlink #. instead of just printf'ing it. #: ../code/operations/operation-export-html.cpp:254 #, c-format msgid "Document generated from %1$s:%2$s at %3$s by %4$s" msgstr "" #: ../code/operations/operation-export-html.cpp:395 msgid "Participants" msgstr "" #: ../code/operations/operation-export-html.cpp:475 msgid "Exporting document \"%1\" to \"%2\" in HTML..." msgstr "" #: ../code/operations/operation-export-html.cpp:536 msgid "Failed to export document \"%1\" to HTML" msgstr "" #: ../code/operations/operation-new.cpp:75 msgid "Creating directory \"%1\"..." msgstr "" #: ../code/operations/operation-new.cpp:76 #, fuzzy msgid "Creating document \"%1\"..." msgstr "Crea un document…" #: ../code/operations/operation-new.cpp:88 msgid "Failed to create directory \"%1\"" msgstr "" #: ../code/operations/operation-new.cpp:89 #, fuzzy msgid "Failed to create document \"%1\"" msgstr "Desa el document actual" #. Translators: This is the 8 bit encoding that is tried when #. autodetecting a file's encoding. #: ../code/operations/operation-open.cpp:38 msgid "ISO-8859-1" msgstr "" #: ../code/operations/operation-open.cpp:114 #, fuzzy msgid "Opening document \"%1\"..." msgstr "Obre un document…" #: ../code/operations/operation-open.cpp:134 msgid "" "The directory into which the new document was supposed to be inserted has " "been removed" msgstr "" #: ../code/operations/operation-open.cpp:323 msgid "The file contains data not in the specified encoding" msgstr "" #: ../code/operations/operation-open.cpp:334 msgid "" "The file either contains data in an unknown encoding, or it contains binary " "data." msgstr "" #: ../code/operations/operation-open-multiple.cpp:91 msgid "Parent folder was removed" msgstr "" #: ../code/operations/operation-open-multiple.cpp:193 #, fuzzy msgid "Failed to open multiple documents" msgstr "Tanca un document obert" #: ../code/operations/operation-save.cpp:97 #, fuzzy msgid "Saving document \"%1\" to \"%2\"..." msgstr "Desa el document «%0%»" #: ../code/operations/operation-save.cpp:226 msgid "" "The document contains one or more characters that cannot be encoded in the " "specified character coding." msgstr "" #: ../code/operations/operation-save.cpp:287 #, fuzzy msgid "Failed to save document \"%1\"" msgstr "Desa el document «%0%»" #: ../code/operations/operation-subscribe-path.cpp:33 msgid "Invalid path: \"%1\"" msgstr "" #: ../code/operations/operation-subscribe-path.cpp:45 #, fuzzy msgid "Invalid path component: \"%1\"" msgstr "Tanca un document obert" #: ../code/operations/operation-subscribe-path.cpp:135 #: ../code/operations/operation-subscribe-path.cpp:200 msgid "Subscribing to \"%1\"..." msgstr "" #: ../code/operations/operation-subscribe-path.cpp:149 #: ../code/operations/operation-subscribe-path.cpp:212 #, fuzzy msgid "Failed to connect to \"%1\"" msgstr "Desa el document actual" #: ../code/operations/operation-subscribe-path.cpp:164 msgid "URI scheme \"%1\" not supported" msgstr "" #: ../code/operations/operation-subscribe-path.cpp:193 #, fuzzy msgid "Connecting to \"%1\"..." msgstr "Crea un document…" #: ../code/operations/operation-subscribe-path.cpp:220 #: ../code/operations/operation-subscribe-path.cpp:319 #: ../code/operations/operation-subscribe-path.cpp:356 #: ../code/operations/operation-subscribe-path.cpp:452 #: ../code/operations/operation-subscribe-path.cpp:472 msgid "Could not subscribe to \"%1\"" msgstr "" #: ../code/operations/operation-subscribe-path.cpp:322 #: ../code/operations/operation-subscribe-path.cpp:358 #, fuzzy msgid "Path \"%1\" does not exist" msgstr "El document %0% no existeix." #: ../code/util/file.cpp:65 ../code/util/file.cpp:74 msgid "Could not create directory \"%1\": %2" msgstr "" #: ../code/util/uri.cpp:91 #, fuzzy msgid "Device \"%1\" does not exist" msgstr "El document %0% no existeix." #: ../code/window.cpp:104 msgid "This Computer" msgstr "" #: ../code/window.cpp:129 #, fuzzy msgid "Document Browser" msgstr "Llista de documents" #: ../code/window.cpp:182 msgid "You are still connected to a session" msgstr "Encara esteu connectat a una sessió" #: ../code/window.cpp:190 #, fuzzy msgid "Do you want to close Gobby nevertheless?" msgstr "Voleu tancar el Gobby igualment?" #: ../code/window.cpp:196 msgid "C_ancel" msgstr "C_ancel·la" #: ../gobby-0.5.desktop.in.h:1 msgid "Gobby" msgstr "" #: ../gobby-0.5.desktop.in.h:2 #, fuzzy msgid "Collaborative Editor" msgstr "Editor col·laboratiu Gobby" #: ../gobby-0.5.desktop.in.h:3 #, fuzzy msgid "Gobby Collaborative Editor (0.5)" msgstr "Editor col·laboratiu Gobby" #: ../gobby-0.5.desktop.in.h:4 msgid "Edit text files collaboratively" msgstr "Editeu fitxers de text col·laborativament" #, fuzzy #~ msgid "" #~ "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.\n" #~ "\n" #~ "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.\n" #~ "\n" #~ "You should have received a copy of the GNU General Public License along " #~ "with this program; if not, write to the Free Software Foundation, Inc., " #~ "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA" #~ msgstr "" #~ "Aquest programa és programari lliure; podeu redistribuir-lo i/o\n" #~ "modificar-lo sota els termes de la Llicència Pública General GNU tal i\n" #~ "com ha estat publicada per la Free Software Foundation; bé sota la\n" #~ "versió 2 de la Llicència o bé (si ho preferiu) sota qualsevol versió\n" #~ "posterior.\n" #~ "\n" #~ "Aquest programa es distribueix amb l'expectativa de que serà útil,\n" #~ "però SENSE CAP GARANTIA; ni tan sols la garantia implícita de\n" #~ "COMERCIABILITAT o ADEQUACIÓ PER UN PROPÒSIT PARTICULAR. Vegeu la\n" #~ "Llicència Pública General GNU per obtenir-ne més detalls." #, fuzzy #~ msgid "Failed to resolve \"%1\": %2" #~ msgstr "Desa el document actual" #~ msgid "_Up" #~ msgstr "A _dalt" #~ msgid "_Down" #~ msgstr "A _baix" #~ msgid "Send" #~ msgstr "Envia" #~ msgid "Command '%0%' not found. Type /help for a list of existing commands." #~ msgstr "" #~ "No s'ha trobat l'ordre '%0%'. Teclegeu /help per llistar les ordres " #~ "existents." #~ msgid "%0%: %1%" #~ msgstr "%0%: %1%" #~ msgid "Usage: /remove " #~ msgstr "Forma d'ús: /remove " #~ msgid "Subscribe" #~ msgstr "Subscriu-me" #~ msgid "GtkSpell error: %0%" #~ msgstr "Error del GtkSpell: %0%" #~ msgid "UTF-8|ISO-8859-1|ISO-8859-15|UTF-7|UTF-16|UCS-2|UCS-4" #~ msgstr "UTF-8|ISO-8859-1|ISO-8859-15|UTF-7|UTF-16|UCS-2|UCS-4" #~ msgid "" #~ "Failed to convert input into UTF-8: Either the encoding is unknown or it " #~ "is binary input." #~ msgstr "" #~ "No s'ha pogut convertir l'entrada a UTF-8: la codificació és desconeguda " #~ "o és entrada binària." #~ msgid "Auto Detect" #~ msgstr "Autodetecta" #~ msgid "Character Encoding:" #~ msgstr "Codificació de caràcters:" #~ msgid "Match as regular expression" #~ msgstr "Fes coincidir com una expressió regular" #~ msgid "Search and replace" #~ msgstr "Cerca i reemplaça" #~ msgid "No document currently opened" #~ msgstr "No hi ha cap document obert" #~ msgid "_Go to line" #~ msgstr "_Vés a la línia" #~ msgid "Join session..." #~ msgstr "Uneix-te a una sessió…" #~ msgid "Joins an existing obby session" #~ msgstr "Uneix a una sessió d'obby ja existent" #~ msgid "Save session" #~ msgstr "Desa la sessió…" #~ msgid "Saves the complete session for a later restore" #~ msgstr "Desa la sessió completa per a restaurar-la més endavant" #~ msgid "Save session as..." #~ msgstr "Anomena i desa la sessió…" #~ msgid "\"Saves as...\" the complete session for a later restore" #~ msgstr "«Anomena i desa…» la sessió completa per a restaurar-la més tard" #~ msgid "Quit session" #~ msgstr "Desconnecta de la sessió" #~ msgid "Leaves the currently running obby session" #~ msgstr "Abandona la sessió d'obby que s'està executant" #~ msgid "Quit" #~ msgstr "Surt" #~ msgid "Quits the application" #~ msgstr "Surt de l'aplicació" #~ msgid "Creates a new document" #~ msgstr "Crea un document nou" #~ msgid "Loads a file into a new document" #~ msgstr "Carrega un fitxer a un document nou" #~ msgid "Saves a document into a file" #~ msgstr "Desa el document en un fitxer" #~ msgid "Save document as..." #~ msgstr "Anomena i desa el document…" #~ msgid "Saves all documents" #~ msgstr "Desa tots els documents" #~ msgid "Close document" #~ msgstr "Tanca el document" #~ msgid "Search for a text and replace it with another one" #~ msgstr "Cerca un text i reemplaça'l amb un altre" #~ msgid "Move cursor to a specified line" #~ msgstr "Mou el cursor a una línia especificada" #~ msgid "Displays a dialog to customize Gobby for your needs" #~ msgstr "" #~ "Mostra un diàleg per a personalitzar el Gobby a les vostres necessitats" #~ msgid "Document preferences..." #~ msgstr "Preferències del document…" #~ msgid "Shows a preferences dialog that is just applied to this document" #~ msgstr "" #~ "Mostra un diàleg de preferèncias que només s'aplica a aquest document" #~ msgid "Syntax" #~ msgstr "Sintaxi" #~ msgid "_User" #~ msgstr "_Usuari" #~ msgid "Sets a password for this user" #~ msgstr "Estableix una contrasenya per a aquest usuari" #~ msgid "Set color..." #~ msgstr "Estableix el color…" #~ msgid "Sets a new color for this user" #~ msgstr "Estableix el color per a aquest usuari" #~ msgid "_Window" #~ msgstr "_Finestra" #~ msgid "Displays a list of users that are currently joined" #~ msgstr "Mostra la llista d'usuaris que estan conectats" #~ msgid "Displays a list of documents within the current session" #~ msgstr "Mostra la llista de documents de dins de la sessió actual" #~ msgid "Displays a chat to talk to other people in the session" #~ msgstr "Mostra el xat per parlar amb l'altra gent a la sessió." #~ msgid "Unselects the current language" #~ msgstr "Desselecciona el llenguatge actual" #~ msgid "Selects %0% as language" #~ msgstr "Selecciona %0% com a llenguatge" #~ msgid "Create obby session" #~ msgstr "Crea una sessió d'obby" #~ msgid "Color:" #~ msgstr "Color:" #~ msgid "Restore session:" #~ msgstr "Restaura la sessió:" #~ msgid "Restore session" #~ msgstr "Restaura una sessió" #~ msgid ".obby files" #~ msgstr "Fitxers .obby" #~ msgid "All files" #~ msgstr "Tots els fitxers" #~ msgid "_Host" #~ msgstr "_Servidor" #~ msgid "Opening obby session..." #~ msgstr "S'està obrint la sessió d'obby…" #~ msgid "Join obby session" #~ msgstr "Connecta a una sessió d'obby" #~ msgid "Host:" #~ msgstr "Servidor:" #~ msgid "Local network" #~ msgstr "Xarxa local" #~ msgid "Host" #~ msgstr "Servidor" #~ msgid "Port" #~ msgstr "Port" #~ msgid "Failure" #~ msgstr "Fallada" #~ msgid "" #~ "User password for user '%0%' required. You may either choose another user " #~ "name, type in your user password or cancel the connection." #~ msgstr "" #~ "Es requereix la contrasenya per l'usuari «%0%». Podeu triar un altre nom " #~ "d'usuari, introduir la vostra contrasenya d'usuari o cancel·lar la " #~ "connexió." #~ msgid "" #~ "Name is already in use. You may choose another name or cancel the " #~ "connection." #~ msgstr "" #~ "El nom ja està en ús. Podeu triar un altre nom o cancel·lar la connexió." #~ msgid "" #~ "Color is already in use. You may choose another color or cancel the " #~ "connection." #~ msgstr "" #~ "El color ja està en ús. Podeu triar un altre color o cancel·lar la " #~ "connexió." #~ msgid "" #~ "Session password required. You have to type in the password to be able to " #~ "join the obby session." #~ msgstr "" #~ "Es requereix una contrasenya per a la sessió. Heu d'introduir la " #~ "contrasenya per a poder unir-vos a la sessió d'obby." #~ msgid "Session password:" #~ msgstr "Contrasenya de la sessió:" #~ msgid "Joining obby session..." #~ msgstr "S'està unint a una sessió d'obby…" #~ msgid "Setting up connection encryption (TLS)..." #~ msgstr "S'està configurant el xifratge (TLS) de la connexió…" #~ msgid "Login packet sent, waiting for response..." #~ msgstr "S'ha enviat el paquet d'entrada, s'està esperant la resposta…" #~ msgid "Logged in successfully, synchronising session..." #~ msgstr "S'ha entrat amb èxit, s'està sincronitzant la sessió…" #~ msgid "Windows" #~ msgstr "Finestres" #~ msgid "Remember the positions and states" #~ msgstr "Recorda les posicions i estats" #~ msgid "Highlight the window on incoming chat messages" #~ msgstr "Realça la finestra quan arriben missatges de xat" #~ msgid "Open new remotely-created documents automatically" #~ msgstr "Obre automàticament els documents nous creats remotament" #~ msgid "Pattern" #~ msgstr "Patró" #~ msgid "Language" #~ msgstr "Llenguatge" #~ msgid "Mime type" #~ msgstr "Tipus MIME" #~ msgid "This is a list of all recognized file types" #~ msgstr "Aquesta es una llista de tots els tipus de fitxer reconeguts" #~ msgid "Pattern must not be empty." #~ msgstr "El patró no pot ser buit" #~ msgid "There is no language with the mime type '%0%'." #~ msgstr "No hi ha cap llenguatge amb el tipus mime «%0%»." #~ msgid "Behaviour" #~ msgstr "Comportament" #~ msgid "Not connected" #~ msgstr "Desconnectat" #~ msgid "Selected language: %0%" #~ msgstr "Llenguatge seleccionat: %0%" #~ msgid "No language selected" #~ msgstr "No hi ha cap llenguatge seleccionat" #~ msgid "Online" #~ msgstr "En línia" #~ msgid "Offline" #~ msgstr "Fora de línia" #~ msgid "" #~ "Zeroconf initialisation failed. Probably you need to run avahi-daemon or " #~ "mDNSResponder, depending on the library you use, as root prior to Gobby. " #~ "Zeroconf support is deactivated for this session." #~ msgstr "" #~ "Ha fallat la inicialització del Zeroconf. És possible que hàgiu " #~ "d'executar l'avahi-daemon o mDNSResponder, depenent de la biblioteca que " #~ "empreu, com a root, abans d'executar el Gobby. S'ha inhabilitat el suport " #~ "de Zeroconf per a aquesta sessió." #~ msgid "Use default .obby extension if none is given" #~ msgstr "Utilitza l'extensió per defecte .obby si no es dóna cap" #~ msgid "Save obby session" #~ msgstr "Desa la sessió d'obby" #~ msgid "A collaborative text editor" #~ msgstr "Un editor de text col·laboratiu" #~ msgid "Create document" #~ msgstr "Crea un document" #~ msgid "Enter document name" #~ msgstr "Introduïu el nom del document" #~ msgid "Open new document" #~ msgstr "Obre un document nou" #~ msgid "" #~ "Click on \"Apply\" to apply the new settings to documents that are " #~ "currently open. \"OK\" will just store the values to use them with newly " #~ "created documents." #~ msgstr "" #~ "Feu clic en «Aplica» per a aplicar els nous paràmetres als documents que " #~ "estan oberts. «D'acord» només emmagatzemarà els valors per a usar-los amb " #~ "els documents de nova creació." #~ msgid "" #~ "Set a user password for your user account. When you try to login next " #~ "time with this user, you will be prompted for your password." #~ msgstr "" #~ "Estableix una contrasenya d'usuari per al vostre compte d'usuari. Quan " #~ "intenteu entrar la pròxima vegada amb aquest usuari, se us demanarà la " #~ "contrasenya." #~ msgid "" #~ "These preferences affect only the currently active document \"%0%\". If " #~ "you want to change global preferences, use the preferences menu item in " #~ "the \"Edit\" menu." #~ msgstr "" #~ "Aquestes preferències només afecten al document actiu «%0%». Si voleu " #~ "canviar les preferències globals, utilitzeu l'element del menú de " #~ "preferències al menú «Edita»." #~ msgid "Color change failed: Color already in use" #~ msgstr "Ha fallat el canvi de color: el color ja està en ús" #~ msgid "Connected to %0%:%1%" #~ msgstr "S'ha connectat a %0%:%1%" #~ msgid "Serving on port %0%" #~ msgstr "S'està servint al port %0%" #~ msgid "Save changes to document \"%0%\" before closing?" #~ msgstr "Voleu desar els canvis al document «%0%» abans de tancar?" #~ msgid "" #~ "If you don't save, changes will be discarded, but may still be retrieved " #~ "if you re-subscribe to the document as long as the session remains open." #~ msgstr "" #~ "Si no deseu, es descartaran els canvis, pero encara podrien ser " #~ "recuperats si us torneu a subscriure al document sempre que la sessió " #~ "encara sigui oberta." #~ msgid "If you don't save, changes will be discarded." #~ msgstr "Si no els deseu, es descartaran tots els canvis." #~ msgid "Close without saving" #~ msgstr "Tanca sense desar" #~ msgid "Set colour..." #~ msgstr "Estableix el color…" #~ msgid "Sets a new colour for this user" #~ msgstr "Estableix el color per a aquest usuari" #~ msgid "Colour:" #~ msgstr "Color:" #~ msgid "Displays a dialog to customise Gobby for your needs" #~ msgstr "" #~ "Mostra un diàleg per a personalitzar el Gobby a les vostres necessitats" #~ msgid "Colour change failed: Colour already in use" #~ msgstr "Ha fallat el canvi de color: el color ja està en ús" #~ msgid "" #~ "Colour is already in use. You may choose another colour or cancel the " #~ "connection." #~ msgstr "" #~ "El color ja està en ús. Podeu triar un altre color o cancel·lar la " #~ "connexió." po/de.po000066400000000000000000001142041304552132100124220ustar00rootroot00000000000000# Gobby German translation # Copyright (C) 2010 Michael Frey # This file is distributed under the same license as the gobby-0.5 package. # Michael Frey , 2010 msgid "" msgstr "" "Project-Id-Version: 0.4.93\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2014-08-23 13:43-0400\n" "PO-Revision-Date: 2010-02-07 09:40+0100\n" "Last-Translator: Michael Frey \n" "Language-Team: \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: ../code/commands/auth-commands.cpp:196 #, fuzzy msgid "Password required" msgstr "Passwort benötigt" #: ../code/commands/auth-commands.cpp:214 msgid "Incorrect password" msgstr "" #: ../code/commands/browser-commands.cpp:288 msgid "Subscribing to %1..." msgstr "Anmeldung bei %1…" #: ../code/commands/browser-commands.cpp:301 msgid "Subscribing to chat on %1..." msgstr "Anmeldung zum Chat bei %1…" #: ../code/commands/browser-commands.cpp:591 msgid "Subscription failed" msgstr "Anmeldung fehlgeschlagen" #: ../code/commands/browser-context-commands.cpp:86 msgid "Could not find a location where to store the certificate" msgstr "" #: ../code/commands/browser-context-commands.cpp:153 msgid "_Remove" msgstr "" #: ../code/commands/browser-context-commands.cpp:188 #, fuzzy msgid "_Disconnect from Server" msgstr "Zu Server verbinden" #: ../code/commands/browser-context-commands.cpp:220 #, fuzzy msgid "Create _Account..." msgstr "Dokument erstellen…" #: ../code/commands/browser-context-commands.cpp:245 msgid "Create Do_cument..." msgstr "Dokument erstellen…" #: ../code/commands/browser-context-commands.cpp:280 #, fuzzy msgid "Create Di_rectory..." msgstr "Verzeichnis erstellen…" #: ../code/commands/browser-context-commands.cpp:293 msgid "_Open Document..." msgstr "Dokument ö_ffnen…" #: ../code/commands/browser-context-commands.cpp:312 msgid "_Permissions..." msgstr "" #: ../code/commands/browser-context-commands.cpp:324 msgid "D_elete" msgstr "_Löschen" #: ../code/commands/browser-context-commands.cpp:405 msgid "Choose a name for the directory" msgstr "Wählen Sie einen Namen für das Verzeichnis" #: ../code/commands/browser-context-commands.cpp:406 msgid "Choose a name for the document" msgstr "Wählen Sie einen Namen für das Dokument:" #: ../code/commands/browser-context-commands.cpp:407 msgid "_Directory Name:" msgstr "_Verzeichnisname:" #: ../code/commands/browser-context-commands.cpp:408 msgid "_Document Name:" msgstr "_Dokumentname:" #: ../code/commands/browser-context-commands.cpp:411 msgid "C_reate" msgstr "E_rstellen" #: ../code/commands/browser-context-commands.cpp:415 msgid "New Directory" msgstr "Neues Verzeichnis" #: ../code/commands/browser-context-commands.cpp:416 #: ../code/commands/file-tasks/task-new.cpp:36 msgid "New Document" msgstr "Neues Dokument" #: ../code/commands/browser-context-commands.cpp:436 #: ../code/commands/file-tasks/task-open-file.cpp:22 msgid "Choose a text file to open" msgstr "Dateien öffnen" #: ../code/commands/browser-context-commands.cpp:558 #: ../code/commands/browser-context-commands.cpp:569 msgid "Account successfully created" msgstr "" #: ../code/commands/browser-context-commands.cpp:562 msgid "When re-connecting to the server, the new account will be used." msgstr "" #: ../code/commands/browser-context-commands.cpp:573 msgid "" "The certificate has been stored at %1.\n" "\n" "To login to this account, set the certificate in Gobby's preferences and re-" "connect to the server." msgstr "" #: ../code/commands/browser-context-commands.cpp:586 #, fuzzy msgid "Failed to create account" msgstr "Erstellen des Dokuments »%1« fehlgeschlagen" #: ../code/commands/browser-context-commands.cpp:589 msgid "Could not save the certificate for the account: %1" msgstr "" #: ../code/commands/file-tasks/task-export-html.cpp:27 msgid "Choose a location to export document \"%1\" to" msgstr "Wählen Sie ein Verzeichnis zum Exportieren der Datei »%1«" #: ../code/commands/file-tasks/task-open.cpp:46 msgid "Querying \"%1\"..." msgstr "»%1« wird abgefragt" #: ../code/commands/file-tasks/task-open.cpp:103 #: ../code/operations/operation-open.cpp:474 #: ../code/operations/operation-open-multiple.cpp:180 msgid "Failed to open document \"%1\"" msgstr "Öffnen des Dokuments fehlschlagen »%1«" #: ../code/commands/file-tasks/task-save.cpp:24 msgid "Choose a location to save document \"%1\" to" msgstr "Wählen Sie ein Verzeichnis zum Speichern der Datei »%1«" #. Help browser could not be invoked, show an error message to the user. #: ../code/commands/help-commands.cpp:70 msgid "There was an error displaying help." msgstr "Beim Anzeigen der Hilfe ist ein Fehler aufgetreten." #: ../code/commands/help-commands.cpp:93 msgid "Contributors:" msgstr "Mitwirkende:" #: ../code/commands/help-commands.cpp:99 msgid "British English:" msgstr "Britisches Englisch:" #: ../code/commands/help-commands.cpp:101 msgid "German:" msgstr "Deutsch:" #: ../code/commands/help-commands.cpp:118 msgid "" "Permission to use, copy, modify, and/or distribute this software for any " "urpose with or without fee is hereby granted, provided that the above " "copyright notice and this permission notice appear in all copies.\n" "\n" "THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES " "WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF " "MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY " "SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES " "WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION " "OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN " "CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE." msgstr "" #: ../code/commands/subscription-commands.cpp:148 msgid "" "The connection to the publisher of this document has been lost. Further " "changes to the document could not be synchronized to others anymore, " "therefore the document cannot be edited anymore.\n" "\n" "Please note also that it is possible that not all of your latest changes " "have reached the publisher before the connection was lost." msgstr "" "Die Verbindung zum Verteiler dieses Dokumentes wurde verloren. Änderungen am " "Dokument können nicht mehr mit anderen abgeglichen werden, daher kann dieses " "Dokument nicht mehr bearbeitet werden.\n" "\n" "Bitte beachten Sie, dass ihre letzten Änderungen möglicherweise nicht mehr " "den Verteiler erreichen konnten, bevor die Verbindung verloren ging." #: ../code/commands/synchronization-commands.cpp:30 msgid "Synchronization in progress... %1%%" msgstr "Abgleich in Arbeit… %1%%" #. Document cannot be used if an error happened #. during synchronization. #: ../code/commands/synchronization-commands.cpp:42 msgid "This document cannot be used." msgstr "Dieses Dokument kann nicht verwendet werden." #: ../code/commands/synchronization-commands.cpp:45 #: ../code/commands/user-join-commands.cpp:43 msgid "" "If you have an idea what could have caused the problem, then you may attempt " "to solve it and try again (after having closed this document). Otherwise it " "is most likely a bug in the software. In that case, please file a bug report " "at http://gobby.0x539.de/trac/newticket and provide as much information as " "you can, including what you did when the problem occurred and how to " "reproduce the problem (if possible) so that we can fix the problem in a " "later version. Thank you." msgstr "" "Falls Sie eine Möglichkeit zur Behebung des Problems sehen, können Sie das " "Dokument schließen und es erneut versuchen. Ansonsten handelt es sich " "wahrscheinlich um einen Programmfehler. In diesem Fall können Sie einen " "Fehlerbericht unter http://gobby.0x539.de/trac/newticket einreichen mit " "möglichst vielen zusätzlichen Informationen, insbesondere, was Sie gerade " "taten, als das Problem auftrat, und, wenn möglich, wie man es reproduzieren " "kann, so dass wir den Fehler für die nächste Version beheben können. Vielen " "Dank." #: ../code/commands/synchronization-commands.cpp:59 msgid "Synchronization failed: %1" msgstr "Abgleich fehlgeschlagen: %1" #. TODO: Adjust this for chat sessions #: ../code/commands/user-join-commands.cpp:38 msgid "" "You can still watch others editing the document, but you cannot edit it " "yourself." msgstr "" "Sie können anderen beim Bearbeiten des Dokuments zusehen, aber nicht selbst " "Änderungen vornehmen." #: ../code/commands/user-join-commands.cpp:57 msgid "User Join failed: %1" msgstr "Anmeldung fehlgeschlagen: %1" #: ../code/commands/user-join-commands.cpp:64 msgid "Permissions are not granted to modify the document." msgstr "" #: ../code/core/browser.cpp:111 msgid "_Direct Connection" msgstr "Direkt_verbindung" #: ../code/core/browser.cpp:113 msgid "Host Name:" msgstr "Servername:" #: ../code/core/certificatemanager.cpp:83 #, c-format msgid "Failed to write Diffie-Hellman parameters to \"%s\": %s" msgstr "" #: ../code/core/certificatemanager.cpp:276 #, c-format msgid "Failed to read Diffie-Hellman parameters: %s" msgstr "" #: ../code/core/certificatemanager.cpp:338 msgid "File does not contain a X.509 certificate" msgstr "" #: ../code/core/certificatemanager.cpp:414 msgid "Certificate does not belong to the chosen key" msgstr "" #: ../code/core/foldermanager.cpp:322 msgid "The document has been removed from the server." msgstr "" #: ../code/core/header.cpp:203 msgid "_None" msgstr "_Keine" #: ../code/core/header.cpp:227 msgid "_File" msgstr "_Datei" #: ../code/core/header.cpp:230 msgid "_Open..." msgstr "Ö_ffnen…" #: ../code/core/header.cpp:233 msgid "Open _Location..." msgstr "_Ort öffnen…" #: ../code/core/header.cpp:237 msgid "Save _As..." msgstr "Speichern _als…" #: ../code/core/header.cpp:241 ../code/core/iconmanager.cpp:43 msgid "Save All" msgstr "Alle speichern" #: ../code/core/header.cpp:241 msgid "Save all open files locally" msgstr "Alle offenen Dateien lokal speichern" #: ../code/core/header.cpp:243 msgid "Export As _HTML..." msgstr "Als _HTML exportieren..." #: ../code/core/header.cpp:246 msgid "Connect _to Server..." msgstr "_Zu Server verbinden…" #: ../code/core/header.cpp:251 msgid "_Edit" msgstr "_Bearbeiten" #: ../code/core/header.cpp:259 msgid "_Find..." msgstr "_Suchen…" #: ../code/core/header.cpp:261 msgid "Find Ne_xt" msgstr "_Weitersuchen" #: ../code/core/header.cpp:262 msgid "Find next match of phrase searched for" msgstr "Suche nach nächstem Vorkommen des Suchbegriffs" #: ../code/core/header.cpp:265 msgid "Find Pre_vious" msgstr "_Rückwärts suchen" #: ../code/core/header.cpp:266 msgid "Find previous match of phrase searched for" msgstr "Suche nach vorigem Vorkommen des Suchbegriffs" #: ../code/core/header.cpp:271 msgid "Find and Rep_lace..." msgstr "Suchen und _Ersetzen…" #: ../code/core/header.cpp:275 msgid "Go to _Line..." msgstr "Gehe zu _Zeile…" #: ../code/core/header.cpp:279 msgid "Pr_eferences..." msgstr "_Einstellungen…" #: ../code/core/header.cpp:281 msgid "_View" msgstr "_Ansicht" #: ../code/core/header.cpp:284 msgid "Reset User Colors" msgstr "Benutzerfarben zurücksetzen" #: ../code/core/header.cpp:285 msgid "Hide user colors in current document" msgstr "Benutzerfarben im aktuellen Dokument nicht anzeigen" #: ../code/core/header.cpp:289 msgid "Fullscreen" msgstr "" #: ../code/core/header.cpp:290 msgid "Show the editing window in fullscreen mode" msgstr "" #: ../code/core/header.cpp:294 msgid "Zoom In" msgstr "" #: ../code/core/header.cpp:297 msgid "Zoom Out" msgstr "" #: ../code/core/header.cpp:300 msgid "View Toolbar" msgstr "Zeige Werkzeugleiste" #: ../code/core/header.cpp:301 msgid "Whether to show the toolbar" msgstr "Ob die Werkzeugleiste angezeigt werden soll" #: ../code/core/header.cpp:305 msgid "View Statusbar" msgstr "Zeige Statusleiste" #: ../code/core/header.cpp:306 msgid "Whether to show the statusbar" msgstr "Ob die Statusleiste angezeigt werden soll" #: ../code/core/header.cpp:310 msgid "View Document Browser" msgstr "Zeige Dokumentenliste" #: ../code/core/header.cpp:311 msgid "Whether to show the document browser" msgstr "Ob die Dokumentenliste angezeigt werden soll" #: ../code/core/header.cpp:315 msgid "View Chat" msgstr "Chat anzeigen" #: ../code/core/header.cpp:316 msgid "Whether to show the chat pane" msgstr "Ob das Chatfenster angezeigt werden soll" #: ../code/core/header.cpp:320 msgid "View Document User List" msgstr "Zeige Dokument-Benutzerliste" #: ../code/core/header.cpp:321 msgid "Whether to show the user list for documents" msgstr "Ob die Benutzerliste für Dokumente angezeigt werden soll" #: ../code/core/header.cpp:325 msgid "View Chat User List" msgstr "Zeige Chat-Benutzerliste" #: ../code/core/header.cpp:326 msgid "Whether to show the user list for the chat" msgstr "Ob die Benutzerliste des Chats angezeigt werden soll" #: ../code/core/header.cpp:330 msgid "_Highlight Mode" msgstr "_Syntaxhervorhebung" #: ../code/core/header.cpp:336 msgid "_Help" msgstr "_Hilfe" #: ../code/core/header.cpp:339 msgid "_Contents" msgstr "_Inhalt" #: ../code/core/header.cpp:340 msgid "Opens the Gobby manual" msgstr "Öffnet die Gobby-Anleitung" #: ../code/core/header.cpp:343 msgid "_About..." msgstr "Ü_ber…" #: ../code/core/header.cpp:344 msgid "Shows Gobby's copyright and credits" msgstr "Zeigt Gobbys Lizenz und Autoren" #: ../code/core/iconmanager.cpp:49 msgid "User list" msgstr "Benutzerliste:" #: ../code/core/iconmanager.cpp:55 msgid "Document list" msgstr "Dokumentenliste" #: ../code/core/iconmanager.cpp:61 ../code/window.cpp:52 msgid "Chat" msgstr "Chat" #: ../code/core/iconmanager.cpp:68 msgid "User Color Indicator" msgstr "Benutzerfarbenindikator" #: ../code/core/selfhoster.cpp:103 msgid "Generating 2048-bit Diffie-Hellman parameters..." msgstr "" #: ../code/core/selfhoster.cpp:135 #, fuzzy msgid "Failed to generate Diffie-Hellman parameters" msgstr "Entfernen des Eintrags »%1« fehlgeschlagen" #: ../code/core/selfhoster.cpp:137 msgid "" "This means that Perfect Forward Secrecy (PFS) is not available. Restart " "Gobby to re-try generating the parameters. The specific error was:\n" "\n" "%1" msgstr "" #: ../code/core/selfhoster.cpp:262 msgid "" "In order to start sharing your documents, choose a private key and " "certificate or create a new pair in the preferences" msgstr "" #: ../code/core/selfhoster.cpp:282 #, fuzzy msgid "Failed to share documents" msgstr "Speichern des Dokuments »%1« fehlgeschlagen" #: ../code/core/sessionuserview.cpp:39 msgid "User List" msgstr "Benutzerliste" #: ../code/core/statusbar.cpp:418 msgid "Ln %1, Col %2\t%3" msgstr "Z. %1, Sp. %2\t%3" #: ../code/core/statusbar.cpp:421 msgid "OVR" msgstr "ÜBS" #: ../code/core/statusbar.cpp:421 msgid "INS" msgstr "EIN" #: ../code/core/textsessionview.cpp:638 msgid "Text written by %1" msgstr "Text geschrieben von %1" #: ../code/core/textsessionview.cpp:644 msgid "Unowned text" msgstr "Kein Verfasser" #: ../code/core/userlist.cpp:108 msgid "Users" msgstr "Benutzer" #: ../code/dialogs/connection-dialog.cpp:26 msgid "Connect to Server" msgstr "Zu Server verbinden" #: ../code/dialogs/connection-dialog.cpp:30 msgid "Please enter a host name with which to establish a connection." msgstr "" "Bitte geben sie den Namen des Servers ein, zu dem Sie verbinden wollen." #: ../code/dialogs/connection-dialog.cpp:32 msgid "_Remote Endpoint:" msgstr "Servername:" #: ../code/dialogs/document-location-dialog.cpp:28 msgid "Document Name:" msgstr "Dokumentnamen:" #: ../code/dialogs/document-location-dialog.cpp:155 msgid "Select document's target location" msgstr "Zielverzeichnis des Dokumentes auswählen" #: ../code/dialogs/document-location-dialog.cpp:157 msgid "Choose a directory to create the document into:" msgstr "Wählen Sie das Verzeichnis, in dem das Dokument erstellt werden soll:" #: ../code/dialogs/document-location-dialog.cpp:163 msgid "Select documents' target location" msgstr "Zielverzeichnis der Dokumente auswählen" #: ../code/dialogs/document-location-dialog.cpp:165 msgid "Choose a directory to create the documents into:" msgstr "" "Wählen Sie das Verzeichnis, in dem die Dokumente erstellt werden sollen:" #: ../code/dialogs/find-dialog.cpp:44 ../code/dialogs/find-dialog.cpp:142 msgid "Find" msgstr "Suche" #: ../code/dialogs/find-dialog.cpp:47 msgid "_Search for:" msgstr "_Suche nach:" #: ../code/dialogs/find-dialog.cpp:49 msgid "Replace _with:" msgstr "Ersetze _mit:" #: ../code/dialogs/find-dialog.cpp:51 msgid "_Match case" msgstr "_Groß-/Kleinschreibung berücksichtigen" #: ../code/dialogs/find-dialog.cpp:52 msgid "Match _entire word only" msgstr "Nur vollständige Wörter berücksichtigen" #: ../code/dialogs/find-dialog.cpp:53 msgid "Search _backwards" msgstr "Rückwärts suchen" #: ../code/dialogs/find-dialog.cpp:54 msgid "Wra_p around" msgstr "Suche beim Erreichen des Endes am Anfang _fortsetzen" #: ../code/dialogs/find-dialog.cpp:100 msgid "Replace _All" msgstr "_Alle ersetzen" #: ../code/dialogs/find-dialog.cpp:102 msgid "_Replace" msgstr "_Ersetzen" #: ../code/dialogs/find-dialog.cpp:142 msgid "Replace" msgstr "Ersetzen" #: ../code/dialogs/find-dialog.cpp:161 ../code/dialogs/find-dialog.cpp:177 msgid "Phrase \"%1\" has not been found" msgstr "Der Ausdruck »%1« wurde nicht gefunden" #: ../code/dialogs/find-dialog.cpp:335 msgid "No occurrence has been replaced" msgstr "Es wurde keine Ersetzung vorgenommen" #: ../code/dialogs/find-dialog.cpp:341 msgid "%1 occurrence has been replaced" msgid_plural "%1 occurrences have been replaced" msgstr[0] "%1 Treffer wurde ersetzt" msgstr[1] "%1 Treffer wurden ersetzt" #: ../code/dialogs/goto-dialog.cpp:26 msgid "Go to line" msgstr "Gehe zu Zeile" #: ../code/dialogs/goto-dialog.cpp:29 msgid "Line _number:" msgstr "Zeilen_nummer" #: ../code/dialogs/goto-dialog.cpp:50 msgid "Go To _Line" msgstr "Gehe zu _Zeile" #: ../code/dialogs/initial-dialog.cpp:76 msgid "Generating 2048-bit RSA private key..." msgstr "" #: ../code/dialogs/initial-dialog.cpp:99 #, fuzzy msgid "Failed to generate private key" msgstr "Entfernen des Eintrags »%1« fehlgeschlagen" #: ../code/dialogs/initial-dialog.cpp:102 msgid "" "%1\n" "\n" "You can try again to create a key in the Security tab of the preferences " "dialog." msgstr "" #: ../code/dialogs/initial-dialog.cpp:141 msgid "Failed to generate self-signed certificate" msgstr "" #: ../code/dialogs/initial-dialog.cpp:144 msgid "" "%1\n" "\n" "You can try again to create a certificate in the Security tab of the " "preferences dialog." msgstr "" #: ../code/dialogs/initial-dialog.cpp:198 msgid "Choose a user color" msgstr "Wählen Sie eine Benutzerfarbe" #: ../code/dialogs/initial-dialog.cpp:204 msgid "Welcome to Gobby" msgstr "Willkommen zu Gobby" #: ../code/dialogs/initial-dialog.cpp:216 msgid "" "Before we start, a few options need to be configured. You can later change " "them by choosing Edit ▸ Preferences from the menu." msgstr "" "Bevor Sie loslegen können, müssen noch einige Einstellungen vorgenommen " "werden. Sie können diese Einstellungen später unter Bearbeiten ▸ " "Einstellungen im Hauptmenü verändern." #: ../code/dialogs/initial-dialog.cpp:224 msgid "User Name" msgstr "Benutzername" #: ../code/dialogs/initial-dialog.cpp:226 msgid "Your name as shown to other users." msgstr "Ihr Name, wie er anderen Benutzern angezeigt wird." #: ../code/dialogs/initial-dialog.cpp:235 msgid "User Color" msgstr "Benutzerfarbe" #: ../code/dialogs/initial-dialog.cpp:237 msgid "The color with which text you have written is branded." msgstr "Die Farbe, in der von Ihnen geschriebener Text hervorgeheben wird." #: ../code/dialogs/initial-dialog.cpp:256 #, fuzzy msgid "Remote Connections" msgstr "Sichere Verbindung" #: ../code/dialogs/initial-dialog.cpp:262 #: ../code/dialogs/preferences-dialog.cpp:217 msgid "Allow remote users to edit local documents" msgstr "" #: ../code/dialogs/initial-dialog.cpp:268 msgid "Ask remote users for a password" msgstr "" #: ../code/dialogs/initial-dialog.cpp:273 #: ../code/dialogs/preferences-dialog.cpp:220 #, fuzzy msgid "Password:" msgstr "Server-Passwort:" #: ../code/dialogs/initial-dialog.cpp:297 msgid "No authentication (Not recommended)" msgstr "" #: ../code/dialogs/initial-dialog.cpp:304 msgid "" "Don't authenticate ourselves to remote users. Data transfer will be " "unencrypted." msgstr "" #: ../code/dialogs/initial-dialog.cpp:313 msgid "Create a self-signed certificate (Recommended)" msgstr "" #: ../code/dialogs/initial-dialog.cpp:321 msgid "" "It may take a minute or two until remote users can connect while the " "security certificate is being generated." msgstr "" #: ../code/dialogs/initial-dialog.cpp:331 msgid "Use an existing certificate (Expert option)" msgstr "" #: ../code/dialogs/initial-dialog.cpp:338 msgid "" "Use an existing private key and certificate for authentication. The files " "must be in PEM format." msgstr "" #: ../code/dialogs/initial-dialog.cpp:347 #: ../code/dialogs/preferences-dialog.cpp:766 msgid "Private key:" msgstr "" #: ../code/dialogs/initial-dialog.cpp:349 #: ../code/dialogs/preferences-dialog.cpp:772 msgid "Certificate:" msgstr "" #: ../code/dialogs/open-location-dialog.cpp:26 msgid "Open Location" msgstr "Ort öffnen" #: ../code/dialogs/open-location-dialog.cpp:27 msgid "Enter the _location (URI) of the file you would like to open:" msgstr "Geben Sie den Ort (URI) der Datei ein, die Sie öffnen möchten:" #: ../code/dialogs/password-dialog.cpp:25 msgid "Password Required" msgstr "Passwort benötigt" #: ../code/dialogs/password-dialog.cpp:31 msgid "Connection to host \"%1\" requires a password." msgstr "Die Verbindung zum Server »%1« erfordert ein Passwort." #: ../code/dialogs/password-dialog.cpp:32 msgid "Invalid password for host \"%1\". Please try again." msgstr "Ungültiges Password für Server »%1«. Bitte versuchen Sie es erneut." #: ../code/dialogs/password-dialog.cpp:35 msgid "Server _Password:" msgstr "Server-Passwort:" #: ../code/dialogs/preferences-dialog.cpp:198 msgid "Settings" msgstr "Einstellungen" #: ../code/dialogs/preferences-dialog.cpp:199 #, fuzzy msgid "Remote Users" msgstr "Benutzerfarben zurücksetzen" #: ../code/dialogs/preferences-dialog.cpp:200 #, fuzzy msgid "Local Documents" msgstr "Neues Dokument" #: ../code/dialogs/preferences-dialog.cpp:202 msgid "User name:" msgstr "Benutzername:" #: ../code/dialogs/preferences-dialog.cpp:204 msgid "User color:" msgstr "Benutzerfarbe:" #: ../code/dialogs/preferences-dialog.cpp:206 msgid "Color intensity:" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:208 msgid "Choose a new user color" msgstr "Neue Benutzerfarbe wählen" #: ../code/dialogs/preferences-dialog.cpp:209 msgid "Show cursors of remote users" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:210 msgid "Show selections of remote users" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:212 #, fuzzy msgid "Highlight current line of remote users" msgstr "Aktuelle Zeile hervorheben" #: ../code/dialogs/preferences-dialog.cpp:214 msgid "Indicate cursor position of remote users in the scrollbar" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:219 msgid "Require remote users to enter a password" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:222 msgid "Port:" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:226 msgid "Remember local documents after Gobby restart" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:227 #, fuzzy msgid "Local documents directory:" msgstr "Sitzungsverzeichnis" #: ../code/dialogs/preferences-dialog.cpp:423 msgid "Tab Stops" msgstr "Tabulatoren" #: ../code/dialogs/preferences-dialog.cpp:424 msgid "Indentation" msgstr "Zeileneinrückung" #: ../code/dialogs/preferences-dialog.cpp:425 msgid "Home/End Behavior" msgstr "Pos1/Ende-Verhalten" #: ../code/dialogs/preferences-dialog.cpp:426 msgid "File Saving" msgstr "Datei speichern" #: ../code/dialogs/preferences-dialog.cpp:427 msgid "_Tab width:" msgstr "_Tabulatorbreite" #: ../code/dialogs/preferences-dialog.cpp:429 msgid "Insert _spaces instead of tabs" msgstr "_Leerzeichen statt Tabulatoren einfügen" #: ../code/dialogs/preferences-dialog.cpp:430 msgid "Enable automatic _indentation" msgstr "Automatische Zeileneinrückung aktivieren" #: ../code/dialogs/preferences-dialog.cpp:431 msgid "Smart _home/end" msgstr "Intelligentes Pos1/Ende" #: ../code/dialogs/preferences-dialog.cpp:432 msgid "Enable _automatic saving of documents" msgstr "Dokumente _automatisch speichern" #: ../code/dialogs/preferences-dialog.cpp:434 msgid "Autosave interval in _minutes:" msgstr "Abstand in Minuten zwischen automatischem Speichern:" #: ../code/dialogs/preferences-dialog.cpp:455 msgid "" "With this option enabled, Home/End keys move to first/last character before " "going to the start/end of the line." msgstr "" "Ist diese Option aktiviert, bewegen die Pos1-/Ende-Taste den Cursor zum " "ersten/letzten Zeichen, bevor sie bis an den Zeilenanfang/das Zeilenende " "gehen." #: ../code/dialogs/preferences-dialog.cpp:525 msgid "Text Wrapping" msgstr "Textumbruch" #: ../code/dialogs/preferences-dialog.cpp:526 msgid "Line Numbers" msgstr "Zeilennummern" #: ../code/dialogs/preferences-dialog.cpp:527 msgid "Current Line" msgstr "Aktuelle Zeile" #: ../code/dialogs/preferences-dialog.cpp:528 msgid "Right Margin" msgstr "Rechter Rand" #: ../code/dialogs/preferences-dialog.cpp:529 msgid "Bracket Matching" msgstr "Klammernpaare" #: ../code/dialogs/preferences-dialog.cpp:530 msgid "Whitespace Display" msgstr "Leerraum" #: ../code/dialogs/preferences-dialog.cpp:531 msgid "Enable text wrapping" msgstr "Zeilenumbruch aktivieren" #: ../code/dialogs/preferences-dialog.cpp:532 msgid "Do not split words over two lines" msgstr "Wörter nicht getrennt über zwei Zeilen schreiben" #: ../code/dialogs/preferences-dialog.cpp:533 msgid "Display line numbers" msgstr "Zeilennummern anzeigen" #: ../code/dialogs/preferences-dialog.cpp:534 msgid "Highlight current line" msgstr "Aktuelle Zeile hervorheben" #: ../code/dialogs/preferences-dialog.cpp:535 msgid "Display right margin" msgstr "Rechter Rand anzeigen" #: ../code/dialogs/preferences-dialog.cpp:536 msgid "Right margin at column:" msgstr "Rechter Rand in Spalte:" #: ../code/dialogs/preferences-dialog.cpp:537 msgid "Highlight matching bracket" msgstr "Klammernpaare hervorheben" #: ../code/dialogs/preferences-dialog.cpp:589 msgid "Display no whitespace" msgstr "Leerräume nicht anzeigen" #: ../code/dialogs/preferences-dialog.cpp:592 msgid "Display spaces" msgstr "Leerzeichen anzeigen" #: ../code/dialogs/preferences-dialog.cpp:596 msgid "Display tabs" msgstr "Tabulatoren anzeigen" #: ../code/dialogs/preferences-dialog.cpp:600 msgid "Display tabs and spaces" msgstr "Tabulatoren und Leerzeichen anzeigen" #: ../code/dialogs/preferences-dialog.cpp:651 msgid "Toolbar" msgstr "Werkzeugleiste" #: ../code/dialogs/preferences-dialog.cpp:652 msgid "Font" msgstr "Schrift" #: ../code/dialogs/preferences-dialog.cpp:653 msgid "Color Scheme" msgstr "Farbschema" #: ../code/dialogs/preferences-dialog.cpp:661 msgid "Show text only" msgstr "Nur Text anzeigen" #: ../code/dialogs/preferences-dialog.cpp:663 msgid "Show icons only" msgstr "Nur Symbole anzeigen" #: ../code/dialogs/preferences-dialog.cpp:665 msgid "Show both icons and text" msgstr "Zeige Symbol und Text" #: ../code/dialogs/preferences-dialog.cpp:667 msgid "Show text besides icons" msgstr "Zeige Text neben dem Symbol" #: ../code/dialogs/preferences-dialog.cpp:756 msgid "Trusted CAs" msgstr "Vertrauenswürdige Zertifizierungsstellen" #: ../code/dialogs/preferences-dialog.cpp:757 msgid "Secure Connection" msgstr "Sichere Verbindung" #: ../code/dialogs/preferences-dialog.cpp:758 #, fuzzy msgid "Authentication" msgstr "Zeileneinrückung" #: ../code/dialogs/preferences-dialog.cpp:759 msgid "Select a file containing trusted CAs" msgstr "" "Wählen Sie eine Datei, die vertrauenswürdige Zertifizierungsstellen enthält" #: ../code/dialogs/preferences-dialog.cpp:764 #, fuzzy msgid "None" msgstr "_Keine" #: ../code/dialogs/preferences-dialog.cpp:765 msgid "Use a certificate" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:767 msgid "Select a private key file" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:769 #: ../code/dialogs/preferences-dialog.cpp:776 #, fuzzy msgid "Create New..." msgstr "Dokument erstellen…" #: ../code/dialogs/preferences-dialog.cpp:773 msgid "Select a certificate file" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:801 msgid "Use TLS if possible" msgstr "Wenn möglich TLS-Verschlüsselung verwenden" #: ../code/dialogs/preferences-dialog.cpp:804 msgid "Always use TLS" msgstr "Immer TLS-Verschlüsselung verwenden" #: ../code/dialogs/preferences-dialog.cpp:892 msgid "Error reading file:" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:916 msgid "2048-bit RSA private key is being generated, please wait..." msgstr "" #: ../code/dialogs/preferences-dialog.cpp:948 msgid "Select a location for the generated key" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:966 msgid "Select a location for the generated certificate" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:1068 msgid "Preferences" msgstr "Einstellungen" #: ../code/dialogs/preferences-dialog.cpp:1073 msgid "User" msgstr "Benutzer" #: ../code/dialogs/preferences-dialog.cpp:1074 msgid "Editor" msgstr "Textbearbeitung" #: ../code/dialogs/preferences-dialog.cpp:1075 msgid "View" msgstr "Ansicht" #: ../code/dialogs/preferences-dialog.cpp:1076 msgid "Appearance" msgstr "Erscheinungsbild" #: ../code/dialogs/preferences-dialog.cpp:1077 msgid "Security" msgstr "Sicherheit" #: ../code/main.cpp:194 msgid "Gobby options" msgstr "Gobby-Optionen" #: ../code/main.cpp:194 msgid "Options related to Gobby" msgstr "Optionen bezüglich Gobby" #: ../code/main.cpp:199 msgid "Display version information and exit" msgstr "Zeige Version von Gobby an" #: ../code/main.cpp:206 msgid "Also start a new Gobby instance when there is one running already" msgstr "Auch neue Gobby-Instanz starten, wenn bereits eine Instanz läuft" #: ../code/main.cpp:214 msgid "Connect to given host on startup, can be given multiple times" msgstr "" "Beim Programmstart zum angegebenen Server verbinden, Mehrfachnennung möglich" #: ../code/main.cpp:215 msgid "HOSTNAME" msgstr "SERVER" #: ../code/operations/operation-delete.cpp:65 msgid "Removing node \"%1\"..." msgstr "Eintrag »%1« wird entfernt…" #: ../code/operations/operation-delete.cpp:75 msgid "Failed to delete node \"%1\"" msgstr "Entfernen des Eintrags »%1« fehlgeschlagen" #: ../code/operations/operation-export-html.cpp:168 #, c-format msgid "written by: %s" msgstr "geschrieben von: %s" #: ../code/operations/operation-export-html.cpp:241 msgid "" msgstr "" #. %1$s is session name/hostname #. %2$s is path within the session #. %3$s is current date as formatted by %c, #. %4$s is a link to the gobby site, it must be present because #. we need to handle that manually to insert a hyperlink #. instead of just printf'ing it. #: ../code/operations/operation-export-html.cpp:254 #, c-format msgid "Document generated from %1$s:%2$s at %3$s by %4$s" msgstr "Dokument erstellt aus %1$s:%2$s am %3$s durch %4$s" #: ../code/operations/operation-export-html.cpp:395 msgid "Participants" msgstr "Teilnehmer" #: ../code/operations/operation-export-html.cpp:475 msgid "Exporting document \"%1\" to \"%2\" in HTML..." msgstr "Exportiere Dokument »%1« nach »%2« als HTML…" #: ../code/operations/operation-export-html.cpp:536 msgid "Failed to export document \"%1\" to HTML" msgstr "Exportieren des Dokuments »%1« zu HTML fehlgeschlagen" #: ../code/operations/operation-new.cpp:75 msgid "Creating directory \"%1\"..." msgstr "Erstelle Verzeichnis »%1«…" #: ../code/operations/operation-new.cpp:76 msgid "Creating document \"%1\"..." msgstr "Erstelle Dokument »%1«…" #: ../code/operations/operation-new.cpp:88 msgid "Failed to create directory \"%1\"" msgstr "Erstellen des Verzeichnisses »%1« fehlgeschlagen" #: ../code/operations/operation-new.cpp:89 msgid "Failed to create document \"%1\"" msgstr "Erstellen des Dokuments »%1« fehlgeschlagen" #. Translators: This is the 8 bit encoding that is tried when #. autodetecting a file's encoding. #: ../code/operations/operation-open.cpp:38 msgid "ISO-8859-1" msgstr "ISO-8859-15" #: ../code/operations/operation-open.cpp:114 msgid "Opening document \"%1\"..." msgstr "Dokument »%1« wird geöffnet…" #: ../code/operations/operation-open.cpp:134 msgid "" "The directory into which the new document was supposed to be inserted has " "been removed" msgstr "" "Das Verzeichnis, in das das neue Dokument eingefügt werden sollte, wurde " "entfernt" #: ../code/operations/operation-open.cpp:323 msgid "The file contains data not in the specified encoding" msgstr "" "Die Datei enthält Daten, die nicht dem angegebenen Zeichensatz entsprechen" #: ../code/operations/operation-open.cpp:334 msgid "" "The file either contains data in an unknown encoding, or it contains binary " "data." msgstr "" "Die Datei enthält entweder Text aus einem unbekannten Zeichensatz oder " "Binärdaten" #: ../code/operations/operation-open-multiple.cpp:91 msgid "Parent folder was removed" msgstr "Enthaltender Ordner wurde entfernt" #: ../code/operations/operation-open-multiple.cpp:193 msgid "Failed to open multiple documents" msgstr "Öffnen mehrerer Dokumente fehlgeschlagen" #: ../code/operations/operation-save.cpp:97 msgid "Saving document \"%1\" to \"%2\"..." msgstr "Speichere Dokument »%1« nach »%2«…" #: ../code/operations/operation-save.cpp:226 msgid "" "The document contains one or more characters that cannot be encoded in the " "specified character coding." msgstr "" "Das Dokument enthält ein oder mehrere Zeichen, die mit dem aktuellen " "Zeichensatz nicht dargestellt werden können." #: ../code/operations/operation-save.cpp:287 msgid "Failed to save document \"%1\"" msgstr "Speichern des Dokuments »%1« fehlgeschlagen" #: ../code/operations/operation-subscribe-path.cpp:33 msgid "Invalid path: \"%1\"" msgstr "" #: ../code/operations/operation-subscribe-path.cpp:45 #, fuzzy msgid "Invalid path component: \"%1\"" msgstr "Öffnen des Dokuments fehlschlagen »%1«" #: ../code/operations/operation-subscribe-path.cpp:135 #: ../code/operations/operation-subscribe-path.cpp:200 #, fuzzy msgid "Subscribing to \"%1\"..." msgstr "Anmeldung bei %1…" #: ../code/operations/operation-subscribe-path.cpp:149 #: ../code/operations/operation-subscribe-path.cpp:212 #, fuzzy msgid "Failed to connect to \"%1\"" msgstr "Erstellen des Verzeichnisses »%1« fehlgeschlagen" #: ../code/operations/operation-subscribe-path.cpp:164 msgid "URI scheme \"%1\" not supported" msgstr "" #: ../code/operations/operation-subscribe-path.cpp:193 #, fuzzy msgid "Connecting to \"%1\"..." msgstr "Verbindung zu »%1« fehlgeschlagen" #: ../code/operations/operation-subscribe-path.cpp:220 #: ../code/operations/operation-subscribe-path.cpp:319 #: ../code/operations/operation-subscribe-path.cpp:356 #: ../code/operations/operation-subscribe-path.cpp:452 #: ../code/operations/operation-subscribe-path.cpp:472 #, fuzzy msgid "Could not subscribe to \"%1\"" msgstr "Auflösung von »%1« fehlgeschlagen" #: ../code/operations/operation-subscribe-path.cpp:322 #: ../code/operations/operation-subscribe-path.cpp:358 #, fuzzy msgid "Path \"%1\" does not exist" msgstr "Schnittstelle »%1« existiert nicht" #: ../code/util/file.cpp:65 ../code/util/file.cpp:74 msgid "Could not create directory \"%1\": %2" msgstr "Erstellen des Verzeichnisses »%1« fehlgeschlagen: %2" #: ../code/util/uri.cpp:91 msgid "Device \"%1\" does not exist" msgstr "Schnittstelle »%1« existiert nicht" #: ../code/window.cpp:104 msgid "This Computer" msgstr "" #: ../code/window.cpp:129 msgid "Document Browser" msgstr "Dokumentenliste" #: ../code/window.cpp:182 msgid "You are still connected to a session" msgstr "Sie sind noch in einer Sitzung" #: ../code/window.cpp:190 msgid "Do you want to close Gobby nevertheless?" msgstr "Wollen Sie Gobby dennoch schließen?" #: ../code/window.cpp:196 msgid "C_ancel" msgstr "_Abbrechen" #: ../gobby-0.5.desktop.in.h:1 msgid "Gobby" msgstr "Gobby" #: ../gobby-0.5.desktop.in.h:2 msgid "Collaborative Editor" msgstr "Gemeinschaftlicher Editor" #: ../gobby-0.5.desktop.in.h:3 msgid "Gobby Collaborative Editor (0.5)" msgstr "Gobby Gemeinschaftlicher Editor (0.5)" #: ../gobby-0.5.desktop.in.h:4 msgid "Edit text files collaboratively" msgstr "Textdateien gemeinsam bearbeiten" #~ msgid "" #~ "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.\n" #~ "\n" #~ "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.\n" #~ "\n" #~ "You should have received a copy of the GNU General Public License along " #~ "with this program; if not, write to the Free Software Foundation, Inc., " #~ "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA" #~ msgstr "" #~ "Dieses Programm ist freie Software. Sie können es unter den Bedingungen " #~ "der GNU General Public License, wie von der Free Software Foundation " #~ "veröffentlicht, weitergeben und/oder modifizieren, entweder gemäß Version " #~ "2 der Lizenz oder (nach Ihrer Option) jeder späteren Version.\n" #~ "\n" #~ "Die Veröffentlichung dieses Programms erfolgt in der Hoffnung, daß es " #~ "Ihnen von Nutzen sein wird, aber OHNE IRGENDEINE GARANTIE, sogar ohne die " #~ "implizite Garantie der MARKTREIFE oder der VERWENDBARKEIT FÜR EINEN " #~ "BESTIMMTEN ZWECK. Details finden Sie in der GNU General Public License.\n" #~ "\n" #~ "Sie sollten ein Exemplar der GNU General Public License zusammen mit " #~ "diesem Programm erhalten haben. Falls nicht, schreiben Sie an die Free " #~ "Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, " #~ "USA." #~ msgid "User Join in progress..." #~ msgstr "Benutzerbeitritt wird bearbeitet…" #, fuzzy #~ msgid "Failed to resolve \"%1\": %2" #~ msgstr "Auflösung von »%1« fehlgeschlagen" #~ msgid "Resolving \"%1\"..." #~ msgstr "Löse »%1« auf…" #~ msgid "Paths" #~ msgstr "Pfade" #~ msgid "" #~ "The directory into which locally hosted sessions are permanently stored" #~ msgstr "" #~ "Verzeichnis zur permanenten Aufbewahrung veröffentlichter Dokuemente" #~ msgid "Subscription already in progress" #~ msgstr "Anmeldung läuft bereits" po/el_GR.po000066400000000000000000001316171304552132100130310ustar00rootroot00000000000000# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # # Yannis Kaskamanidis , 2010. msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2014-08-23 13:43-0400\n" "PO-Revision-Date: 2010-06-20 17:27+0300\n" "Last-Translator: Yannis Kaskamanidis \n" "Language-Team: Ελληνικά \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Lokalize 1.0\n" #: ../code/commands/auth-commands.cpp:196 #, fuzzy msgid "Password required" msgstr "Απαιτείται κωδικός πρόσβασης" #: ../code/commands/auth-commands.cpp:214 msgid "Incorrect password" msgstr "" #: ../code/commands/browser-commands.cpp:288 msgid "Subscribing to %1..." msgstr "Εγγραφή στο %1..." #: ../code/commands/browser-commands.cpp:301 msgid "Subscribing to chat on %1..." msgstr "Εγγραφή στη συνομιλία %1..." #: ../code/commands/browser-commands.cpp:591 msgid "Subscription failed" msgstr "Η εγγραφή απέτυχε" #: ../code/commands/browser-context-commands.cpp:86 msgid "Could not find a location where to store the certificate" msgstr "" #: ../code/commands/browser-context-commands.cpp:153 msgid "_Remove" msgstr "" #: ../code/commands/browser-context-commands.cpp:188 #, fuzzy msgid "_Disconnect from Server" msgstr "Σύνδεση στον διακομιστή" #: ../code/commands/browser-context-commands.cpp:220 #, fuzzy msgid "Create _Account..." msgstr "Δημιουργία εγγράφου..." #: ../code/commands/browser-context-commands.cpp:245 msgid "Create Do_cument..." msgstr "Δημιουργία εγγράφου..." #: ../code/commands/browser-context-commands.cpp:280 #, fuzzy msgid "Create Di_rectory..." msgstr "Δημιουργία φακέλου..." #: ../code/commands/browser-context-commands.cpp:293 msgid "_Open Document..." msgstr "Άνοιγμα εγγράφου..." #: ../code/commands/browser-context-commands.cpp:312 msgid "_Permissions..." msgstr "" #: ../code/commands/browser-context-commands.cpp:324 msgid "D_elete" msgstr "Διαγραφή" #: ../code/commands/browser-context-commands.cpp:405 msgid "Choose a name for the directory" msgstr "Επιλογή ονόματος για τον φάκελο" #: ../code/commands/browser-context-commands.cpp:406 msgid "Choose a name for the document" msgstr "Επιλογή ονόματος για το έγγραφο" #: ../code/commands/browser-context-commands.cpp:407 msgid "_Directory Name:" msgstr "Όνομα φακέλου:" #: ../code/commands/browser-context-commands.cpp:408 msgid "_Document Name:" msgstr "Όνομα εγγράφου:" #: ../code/commands/browser-context-commands.cpp:411 msgid "C_reate" msgstr "Δημιουργία" #: ../code/commands/browser-context-commands.cpp:415 msgid "New Directory" msgstr "Νέος φάκελος" #: ../code/commands/browser-context-commands.cpp:416 #: ../code/commands/file-tasks/task-new.cpp:36 msgid "New Document" msgstr "Νέο έγγραφο" #: ../code/commands/browser-context-commands.cpp:436 #: ../code/commands/file-tasks/task-open-file.cpp:22 msgid "Choose a text file to open" msgstr "Επιλογή ενός αρχείου κειμένου για άνοιγμα" #: ../code/commands/browser-context-commands.cpp:558 #: ../code/commands/browser-context-commands.cpp:569 msgid "Account successfully created" msgstr "" #: ../code/commands/browser-context-commands.cpp:562 msgid "When re-connecting to the server, the new account will be used." msgstr "" #: ../code/commands/browser-context-commands.cpp:573 msgid "" "The certificate has been stored at %1.\n" "\n" "To login to this account, set the certificate in Gobby's preferences and re-" "connect to the server." msgstr "" #: ../code/commands/browser-context-commands.cpp:586 #, fuzzy msgid "Failed to create account" msgstr "Αποτυχία δημιουργίας εγγράφου \"%1\"" #: ../code/commands/browser-context-commands.cpp:589 msgid "Could not save the certificate for the account: %1" msgstr "" #: ../code/commands/file-tasks/task-export-html.cpp:27 msgid "Choose a location to export document \"%1\" to" msgstr "Επιλογή τοποθεσίας για εξαγωγή του εγγράφου \"%1\" στο" #: ../code/commands/file-tasks/task-open.cpp:46 msgid "Querying \"%1\"..." msgstr "Ερώτημα \"%1\"..." #: ../code/commands/file-tasks/task-open.cpp:103 #: ../code/operations/operation-open.cpp:474 #: ../code/operations/operation-open-multiple.cpp:180 msgid "Failed to open document \"%1\"" msgstr "Αποτυχία ανοίγματος του εγγράφου \"%1\"" #: ../code/commands/file-tasks/task-save.cpp:24 msgid "Choose a location to save document \"%1\" to" msgstr "Επιλογή τοποθεσίας για αποθήκευση του εγγράφου \"%1\" στο" #. Help browser could not be invoked, show an error message to the user. #: ../code/commands/help-commands.cpp:70 msgid "There was an error displaying help." msgstr "Υπήρξε κάποιο σφάλμα κατά την προβολή της βοήθειας." #: ../code/commands/help-commands.cpp:93 msgid "Contributors:" msgstr "Άτομα που συνεισέφεραν:" #: ../code/commands/help-commands.cpp:99 msgid "British English:" msgstr "Αγγλικά (Βρετανίας):" #: ../code/commands/help-commands.cpp:101 msgid "German:" msgstr "Γερμανικά:" #: ../code/commands/help-commands.cpp:118 msgid "" "Permission to use, copy, modify, and/or distribute this software for any " "urpose with or without fee is hereby granted, provided that the above " "copyright notice and this permission notice appear in all copies.\n" "\n" "THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES " "WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF " "MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY " "SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES " "WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION " "OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN " "CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE." msgstr "" #: ../code/commands/subscription-commands.cpp:148 msgid "" "The connection to the publisher of this document has been lost. Further " "changes to the document could not be synchronized to others anymore, " "therefore the document cannot be edited anymore.\n" "\n" "Please note also that it is possible that not all of your latest changes " "have reached the publisher before the connection was lost." msgstr "" "Η σύνδεση με τον εκδότη αυτού του εγγράφου χάθηκε. Περισσότερες αλλαγές στο " "έγγραφο δεν είναι πλέον δυνατόν να συγχρονιστούν με άλλους χρήστες. Η " "επεξεργασία του εγγράφου δεν είναι πια δυνατή.\n" "\n" "Παρακαλώ σημειώστε ότι είναι δυνατόν να μην έχουν φθάσει στον εκδότη όλες οι " "τελευταίες αλλαγές πριν τη διακοπή της σύνδεσης." #: ../code/commands/synchronization-commands.cpp:30 msgid "Synchronization in progress... %1%%" msgstr "Συγχρονισμός σε εξέλιξη... %1%%" #. Document cannot be used if an error happened #. during synchronization. #: ../code/commands/synchronization-commands.cpp:42 msgid "This document cannot be used." msgstr "Αυτό το έγγραφο δεν μπορεί να χρησιμοποιηθεί." #: ../code/commands/synchronization-commands.cpp:45 #: ../code/commands/user-join-commands.cpp:43 msgid "" "If you have an idea what could have caused the problem, then you may attempt " "to solve it and try again (after having closed this document). Otherwise it " "is most likely a bug in the software. In that case, please file a bug report " "at http://gobby.0x539.de/trac/newticket and provide as much information as " "you can, including what you did when the problem occurred and how to " "reproduce the problem (if possible) so that we can fix the problem in a " "later version. Thank you." msgstr "" "Εάν υποψιάζεστε τι μπορεί να προκάλεσε το πρόβλημα, προσπαθήστε να το λύσετε " "και ξαναπροσπαθήστε (αφού έχετε κλείσει το έγγραφο). Διαφορετικά, πιθανόν να " "πρόκειται για κάποιο σφάλμα του λογισμικού. Σε αυτή την περίπτωση, στείλτε " "μια αναφορά σφάλματος στο http://gobby.0x539.de/trac/newticket και προσθέστε " "όσες περισσότερες πληροφορίες μπορείτε, καθώς και το τι κάνατε όταν συνέβη " "το πρόβλημα. Επίσης, δώστε πληροφορίες σχετικά με το πώς μπορεί να γίνει " "αναπαραγωγή του προβλήματος (αν κάτι τέτοιο είναι εφικτό), ώστε να " "διορθώσουμε το πρόβλημα σε μια επόμενη έκδοση. Ευχαριστούμε." #: ../code/commands/synchronization-commands.cpp:59 msgid "Synchronization failed: %1" msgstr "Ο συγχρονισμός απέτυχε: %1" #. TODO: Adjust this for chat sessions #: ../code/commands/user-join-commands.cpp:38 msgid "" "You can still watch others editing the document, but you cannot edit it " "yourself." msgstr "" "Μπορείτε ακόμη να παρακολουθείτε άλλους να επεξεργάζονται το έγγραφο, αλλά " "δεν μπορείτε εσείς να συμμετέχετε στην επεξεργασία." #: ../code/commands/user-join-commands.cpp:57 msgid "User Join failed: %1" msgstr "Η σύνδεση του χρήστη απέτυχε: %1" #: ../code/commands/user-join-commands.cpp:64 msgid "Permissions are not granted to modify the document." msgstr "" #: ../code/core/browser.cpp:111 msgid "_Direct Connection" msgstr "Άμεση σύνδεση" #: ../code/core/browser.cpp:113 msgid "Host Name:" msgstr "Όνομα κόμβου:" #: ../code/core/certificatemanager.cpp:83 #, c-format msgid "Failed to write Diffie-Hellman parameters to \"%s\": %s" msgstr "" #: ../code/core/certificatemanager.cpp:276 #, c-format msgid "Failed to read Diffie-Hellman parameters: %s" msgstr "" #: ../code/core/certificatemanager.cpp:338 msgid "File does not contain a X.509 certificate" msgstr "" #: ../code/core/certificatemanager.cpp:414 msgid "Certificate does not belong to the chosen key" msgstr "" #: ../code/core/foldermanager.cpp:322 msgid "The document has been removed from the server." msgstr "" #: ../code/core/header.cpp:203 msgid "_None" msgstr "Κανένα" #: ../code/core/header.cpp:227 msgid "_File" msgstr "Αρχείο" #: ../code/core/header.cpp:230 msgid "_Open..." msgstr "Άνοιγμα..." #: ../code/core/header.cpp:233 msgid "Open _Location..." msgstr "Άνοιγμα τοποθεσίας..." #: ../code/core/header.cpp:237 msgid "Save _As..." msgstr "Αποθήκευση ως..." #: ../code/core/header.cpp:241 ../code/core/iconmanager.cpp:43 msgid "Save All" msgstr "Αποθήκευση όλων" #: ../code/core/header.cpp:241 msgid "Save all open files locally" msgstr "Αποθήκευση όλων των ανοιχτών αρχείων τοπικά" #: ../code/core/header.cpp:243 msgid "Export As _HTML..." msgstr "Εξαγωγή ως HTML..." #: ../code/core/header.cpp:246 msgid "Connect _to Server..." msgstr "Σύνδεση στον διακομιστή..." #: ../code/core/header.cpp:251 msgid "_Edit" msgstr "Επεξεργασία" #: ../code/core/header.cpp:259 msgid "_Find..." msgstr "Εύρεση..." #: ../code/core/header.cpp:261 msgid "Find Ne_xt" msgstr "Εύρεση επόμενου" #: ../code/core/header.cpp:262 msgid "Find next match of phrase searched for" msgstr "Εύρεση επόμενης φράσης που ταιριάζει με αυτήν που αναζητάτε" #: ../code/core/header.cpp:265 msgid "Find Pre_vious" msgstr "Εύρεση προηγούμενου" #: ../code/core/header.cpp:266 msgid "Find previous match of phrase searched for" msgstr "Εύρεση προηγούμενης φράσης που ταιριάζει με αυτήν που αναζητάτε" #: ../code/core/header.cpp:271 msgid "Find and Rep_lace..." msgstr "Εύρεση και αντικατάσταση..." #: ../code/core/header.cpp:275 msgid "Go to _Line..." msgstr "Μετάβαση στη γραμμή..." #: ../code/core/header.cpp:279 msgid "Pr_eferences..." msgstr "Προτιμήσεις..." #: ../code/core/header.cpp:281 msgid "_View" msgstr "Προβολή" #: ../code/core/header.cpp:284 msgid "Reset User Colors" msgstr "Επαναφορά χρωμάτων χρήστη" #: ../code/core/header.cpp:285 msgid "Hide user colors in current document" msgstr "Απόκρυψη χρωμάτων χρήστη στο τρέχον έγγραφο" #: ../code/core/header.cpp:289 msgid "Fullscreen" msgstr "" #: ../code/core/header.cpp:290 msgid "Show the editing window in fullscreen mode" msgstr "" #: ../code/core/header.cpp:294 msgid "Zoom In" msgstr "" #: ../code/core/header.cpp:297 msgid "Zoom Out" msgstr "" #: ../code/core/header.cpp:300 msgid "View Toolbar" msgstr "Προβολή γραμμής εργαλείων" #: ../code/core/header.cpp:301 msgid "Whether to show the toolbar" msgstr "Αν θα εμφανίζεται η γραμμή εργαλείων" #: ../code/core/header.cpp:305 msgid "View Statusbar" msgstr "Προβολή γραμμής κατάστασης" #: ../code/core/header.cpp:306 msgid "Whether to show the statusbar" msgstr "Αν θα εμφανίζεται η γραμμή κατάστασης" #: ../code/core/header.cpp:310 msgid "View Document Browser" msgstr "Προβολή περιηγητή εγγράφου" #: ../code/core/header.cpp:311 msgid "Whether to show the document browser" msgstr "Αν θα εμφανίζεται ο περιηγητής εγγράφου" #: ../code/core/header.cpp:315 msgid "View Chat" msgstr "Προβολή συνομιλίας" #: ../code/core/header.cpp:316 msgid "Whether to show the chat pane" msgstr "Εάν θα εμφανίζεται το παράθυρο συνομιλίας" #: ../code/core/header.cpp:320 msgid "View Document User List" msgstr "Προβολή καταλόγου χρηστών του εγγράφου" #: ../code/core/header.cpp:321 msgid "Whether to show the user list for documents" msgstr "Εάν θα εμφανίζεται ο κατάλογος χρηστών για τα έγγραφα" #: ../code/core/header.cpp:325 msgid "View Chat User List" msgstr "Προβολή καταλόγου χρηστών συνομιλίας" #: ../code/core/header.cpp:326 msgid "Whether to show the user list for the chat" msgstr "Εάν θα εμφανίζεται ο κατάλογος χρηστών της συνομιλίας" #: ../code/core/header.cpp:330 msgid "_Highlight Mode" msgstr "Λειτουργία επισήμανσης" #: ../code/core/header.cpp:336 msgid "_Help" msgstr "Βοήθεια" #: ../code/core/header.cpp:339 msgid "_Contents" msgstr "Περιεχόμενα" #: ../code/core/header.cpp:340 msgid "Opens the Gobby manual" msgstr "Ανοίγει το εγχειρίδιο του Gobby" #: ../code/core/header.cpp:343 msgid "_About..." msgstr "Σχετικά..." #: ../code/core/header.cpp:344 msgid "Shows Gobby's copyright and credits" msgstr "Εμφανίζει τα πνευματικά δικαιώματα και τα εύσημα για το Gobby" #: ../code/core/iconmanager.cpp:49 msgid "User list" msgstr "Κατάλογος χρηστών" #: ../code/core/iconmanager.cpp:55 msgid "Document list" msgstr "Κατάλογος εγγράφων" #: ../code/core/iconmanager.cpp:61 ../code/window.cpp:52 msgid "Chat" msgstr "Συνομιλία" #: ../code/core/iconmanager.cpp:68 msgid "User Color Indicator" msgstr "Ένδειξη χρώματος χρήστη" #: ../code/core/selfhoster.cpp:103 msgid "Generating 2048-bit Diffie-Hellman parameters..." msgstr "" #: ../code/core/selfhoster.cpp:135 #, fuzzy msgid "Failed to generate Diffie-Hellman parameters" msgstr "Αποτυχία διαγραφής διακομιστή %1: %2" #: ../code/core/selfhoster.cpp:137 msgid "" "This means that Perfect Forward Secrecy (PFS) is not available. Restart " "Gobby to re-try generating the parameters. The specific error was:\n" "\n" "%1" msgstr "" #: ../code/core/selfhoster.cpp:262 msgid "" "In order to start sharing your documents, choose a private key and " "certificate or create a new pair in the preferences" msgstr "" #: ../code/core/selfhoster.cpp:282 #, fuzzy msgid "Failed to share documents" msgstr "Αποτυχία αποθήκευσης εγγράφου \"%1\"" #: ../code/core/sessionuserview.cpp:39 msgid "User List" msgstr "Κατάλογος χρηστών" #: ../code/core/statusbar.cpp:418 msgid "Ln %1, Col %2\t%3" msgstr "Ln %1, Col %2\t%3" #: ../code/core/statusbar.cpp:421 msgid "OVR" msgstr "OVR" #: ../code/core/statusbar.cpp:421 msgid "INS" msgstr "INS" #: ../code/core/textsessionview.cpp:638 msgid "Text written by %1" msgstr "Κείμενο που συντάχθηκε από τον/την %1" #: ../code/core/textsessionview.cpp:644 msgid "Unowned text" msgstr "Κείμενο που δεν ανήκει σε κανέναν" #: ../code/core/userlist.cpp:108 msgid "Users" msgstr "Χρήστες" #: ../code/dialogs/connection-dialog.cpp:26 msgid "Connect to Server" msgstr "Σύνδεση στον διακομιστή" #: ../code/dialogs/connection-dialog.cpp:30 msgid "Please enter a host name with which to establish a connection." msgstr "" "Παρακαλώ εισάγετε το όνομα του διακομιστή με τον οποίο θα γίνει η σύνδεση." #: ../code/dialogs/connection-dialog.cpp:32 msgid "_Remote Endpoint:" msgstr "Απομακρυσμένο τελικό σημείο:" #: ../code/dialogs/document-location-dialog.cpp:28 msgid "Document Name:" msgstr "Όνομα εγγράφου:" #: ../code/dialogs/document-location-dialog.cpp:155 msgid "Select document's target location" msgstr "Επιλογή θέσης προορισμού του εγγράφου" #: ../code/dialogs/document-location-dialog.cpp:157 msgid "Choose a directory to create the document into:" msgstr "Επιλογή φακέλου για δημιουργία εγγράφου μέσα σ' αυτόν:" #: ../code/dialogs/document-location-dialog.cpp:163 msgid "Select documents' target location" msgstr "Επιλογή θέσης προορισμού του εγγράφου" #: ../code/dialogs/document-location-dialog.cpp:165 msgid "Choose a directory to create the documents into:" msgstr "Επιλογή φακέλου για δημιουργία εγγράφου μέσα σ' αυτόν:" #: ../code/dialogs/find-dialog.cpp:44 ../code/dialogs/find-dialog.cpp:142 msgid "Find" msgstr "Εύρεση" #: ../code/dialogs/find-dialog.cpp:47 msgid "_Search for:" msgstr "Εύρεση για:" #: ../code/dialogs/find-dialog.cpp:49 msgid "Replace _with:" msgstr "Αντικατάσταση με:" #: ../code/dialogs/find-dialog.cpp:51 msgid "_Match case" msgstr "Ταίριασμα" #: ../code/dialogs/find-dialog.cpp:52 msgid "Match _entire word only" msgstr "Ταίριασμα μόνον ολόκληρης λέξης" #: ../code/dialogs/find-dialog.cpp:53 msgid "Search _backwards" msgstr "Αναζήτηση προς τα πίσω" #: ../code/dialogs/find-dialog.cpp:54 msgid "Wra_p around" msgstr "Αναδίπλωση ολόγυρα" #: ../code/dialogs/find-dialog.cpp:100 msgid "Replace _All" msgstr "Αντικατάσταση όλων" #: ../code/dialogs/find-dialog.cpp:102 msgid "_Replace" msgstr "Αντικατάσταση" #: ../code/dialogs/find-dialog.cpp:142 msgid "Replace" msgstr "Αντικατάσταση" #: ../code/dialogs/find-dialog.cpp:161 ../code/dialogs/find-dialog.cpp:177 msgid "Phrase \"%1\" has not been found" msgstr "Η φράση \"%1\" δεν βρέθηκε" #: ../code/dialogs/find-dialog.cpp:335 msgid "No occurrence has been replaced" msgstr "Τίποτε δεν αντικαταστάθηκε" #: ../code/dialogs/find-dialog.cpp:341 msgid "%1 occurrence has been replaced" msgid_plural "%1 occurrences have been replaced" msgstr[0] "%1 αντικαταστάθηκε" msgstr[1] "%1 αντικαταστάθηκαν" #: ../code/dialogs/goto-dialog.cpp:26 msgid "Go to line" msgstr "Μετάβαση στη γραμμή" #: ../code/dialogs/goto-dialog.cpp:29 msgid "Line _number:" msgstr "Αριθμός γραμμής:" #: ../code/dialogs/goto-dialog.cpp:50 msgid "Go To _Line" msgstr "Μετάβαση στη γραμμή" #: ../code/dialogs/initial-dialog.cpp:76 msgid "Generating 2048-bit RSA private key..." msgstr "" #: ../code/dialogs/initial-dialog.cpp:99 #, fuzzy msgid "Failed to generate private key" msgstr "Αποτυχία διαγραφής διακομιστή %1: %2" #: ../code/dialogs/initial-dialog.cpp:102 msgid "" "%1\n" "\n" "You can try again to create a key in the Security tab of the preferences " "dialog." msgstr "" #: ../code/dialogs/initial-dialog.cpp:141 msgid "Failed to generate self-signed certificate" msgstr "" #: ../code/dialogs/initial-dialog.cpp:144 msgid "" "%1\n" "\n" "You can try again to create a certificate in the Security tab of the " "preferences dialog." msgstr "" #: ../code/dialogs/initial-dialog.cpp:198 msgid "Choose a user color" msgstr "Επιλογή χρώματος χρήστη" #: ../code/dialogs/initial-dialog.cpp:204 msgid "Welcome to Gobby" msgstr "Καλωσήρθατε στο Gobby" #: ../code/dialogs/initial-dialog.cpp:216 msgid "" "Before we start, a few options need to be configured. You can later change " "them by choosing Edit ▸ Preferences from the menu." msgstr "" "Πριν ξεκινήσουμε, καλό είναι να ρυθμίσουμε ορισμένες επιλογές. Αργότερα " "μπορούμε να τις αλλάξουμε επιλέγοντας το μενού Επεξεργασία ▸ Προτιμήσεις." #: ../code/dialogs/initial-dialog.cpp:224 msgid "User Name" msgstr "Όνομα χρήστη" #: ../code/dialogs/initial-dialog.cpp:226 msgid "Your name as shown to other users." msgstr "Το όνομά σας όπως το βλέπουν οι άλλοι χρήστες" #: ../code/dialogs/initial-dialog.cpp:235 msgid "User Color" msgstr "Χρώμα χρήστη" #: ../code/dialogs/initial-dialog.cpp:237 msgid "The color with which text you have written is branded." msgstr "Το χρώμα με το οποίο έχετε γράψει το κείμενό σας είναι επώνυμο." #: ../code/dialogs/initial-dialog.cpp:256 #, fuzzy msgid "Remote Connections" msgstr "Ασφαλής σύνδεση" #: ../code/dialogs/initial-dialog.cpp:262 #: ../code/dialogs/preferences-dialog.cpp:217 msgid "Allow remote users to edit local documents" msgstr "" #: ../code/dialogs/initial-dialog.cpp:268 msgid "Ask remote users for a password" msgstr "" #: ../code/dialogs/initial-dialog.cpp:273 #: ../code/dialogs/preferences-dialog.cpp:220 #, fuzzy msgid "Password:" msgstr "Κωδικός πρόσβασης διακομιστή:" #: ../code/dialogs/initial-dialog.cpp:297 msgid "No authentication (Not recommended)" msgstr "" #: ../code/dialogs/initial-dialog.cpp:304 msgid "" "Don't authenticate ourselves to remote users. Data transfer will be " "unencrypted." msgstr "" #: ../code/dialogs/initial-dialog.cpp:313 msgid "Create a self-signed certificate (Recommended)" msgstr "" #: ../code/dialogs/initial-dialog.cpp:321 msgid "" "It may take a minute or two until remote users can connect while the " "security certificate is being generated." msgstr "" #: ../code/dialogs/initial-dialog.cpp:331 msgid "Use an existing certificate (Expert option)" msgstr "" #: ../code/dialogs/initial-dialog.cpp:338 msgid "" "Use an existing private key and certificate for authentication. The files " "must be in PEM format." msgstr "" #: ../code/dialogs/initial-dialog.cpp:347 #: ../code/dialogs/preferences-dialog.cpp:766 msgid "Private key:" msgstr "" #: ../code/dialogs/initial-dialog.cpp:349 #: ../code/dialogs/preferences-dialog.cpp:772 msgid "Certificate:" msgstr "" #: ../code/dialogs/open-location-dialog.cpp:26 msgid "Open Location" msgstr "Άνοιγμα τοποθεσίας" #: ../code/dialogs/open-location-dialog.cpp:27 msgid "Enter the _location (URI) of the file you would like to open:" msgstr "Εισαγωγή της τοποθεσίας (URI) του αρχείου που θέλετε να ανοίξετε:" #: ../code/dialogs/password-dialog.cpp:25 msgid "Password Required" msgstr "Απαιτείται κωδικός πρόσβασης" #: ../code/dialogs/password-dialog.cpp:31 msgid "Connection to host \"%1\" requires a password." msgstr "Η σύνδεση με τον διακομιστή \"%1\" απαιτεί κωδικό πρόσβασης." #: ../code/dialogs/password-dialog.cpp:32 msgid "Invalid password for host \"%1\". Please try again." msgstr "" "Μη έγκυρος κωδικός πρόσβασης για τον διακομιστή \"%1\". Παρακαλώ προσπαθήστε " "πάλι." #: ../code/dialogs/password-dialog.cpp:35 msgid "Server _Password:" msgstr "Κωδικός πρόσβασης διακομιστή:" #: ../code/dialogs/preferences-dialog.cpp:198 msgid "Settings" msgstr "Ρυθμίσεις" #: ../code/dialogs/preferences-dialog.cpp:199 #, fuzzy msgid "Remote Users" msgstr "Επαναφορά χρωμάτων χρήστη" #: ../code/dialogs/preferences-dialog.cpp:200 #, fuzzy msgid "Local Documents" msgstr "Νέο έγγραφο" #: ../code/dialogs/preferences-dialog.cpp:202 msgid "User name:" msgstr "Όνομα χρήστη:" #: ../code/dialogs/preferences-dialog.cpp:204 msgid "User color:" msgstr "Χρώμα χρήστη:" #: ../code/dialogs/preferences-dialog.cpp:206 msgid "Color intensity:" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:208 msgid "Choose a new user color" msgstr "Επιλογή νέου χρώματος χρήστη" #: ../code/dialogs/preferences-dialog.cpp:209 msgid "Show cursors of remote users" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:210 msgid "Show selections of remote users" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:212 #, fuzzy msgid "Highlight current line of remote users" msgstr "Επισήμανση τρέχουσας γραμμής" #: ../code/dialogs/preferences-dialog.cpp:214 msgid "Indicate cursor position of remote users in the scrollbar" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:219 msgid "Require remote users to enter a password" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:222 msgid "Port:" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:226 msgid "Remember local documents after Gobby restart" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:227 #, fuzzy msgid "Local documents directory:" msgstr "Φάκελος κόμβου:" #: ../code/dialogs/preferences-dialog.cpp:423 msgid "Tab Stops" msgstr "Στηλοθέτες" #: ../code/dialogs/preferences-dialog.cpp:424 msgid "Indentation" msgstr "Εσοχή" #: ../code/dialogs/preferences-dialog.cpp:425 msgid "Home/End Behavior" msgstr "Συμπεριφορά πλήκτρων Home/End" #: ../code/dialogs/preferences-dialog.cpp:426 msgid "File Saving" msgstr "Αποθήκευση αρχείου" #: ../code/dialogs/preferences-dialog.cpp:427 msgid "_Tab width:" msgstr "Πλάτος στηλοθέτη:" #: ../code/dialogs/preferences-dialog.cpp:429 msgid "Insert _spaces instead of tabs" msgstr "Εισαγωγή διαστημάτων αντί και στηλοθέτες" #: ../code/dialogs/preferences-dialog.cpp:430 msgid "Enable automatic _indentation" msgstr "Ενεργοποίηση αυτόματης εισαγωγής εσοχών" #: ../code/dialogs/preferences-dialog.cpp:431 msgid "Smart _home/end" msgstr "Έξυπνο Home/End" #: ../code/dialogs/preferences-dialog.cpp:432 msgid "Enable _automatic saving of documents" msgstr "Ενεργοποίηση αυτόματης αποθήκευσης εγγράφων" #: ../code/dialogs/preferences-dialog.cpp:434 msgid "Autosave interval in _minutes:" msgstr "Χρονικό διάστημα αυτόματης αποθήκευσης σε λεπτά:" #: ../code/dialogs/preferences-dialog.cpp:455 msgid "" "With this option enabled, Home/End keys move to first/last character before " "going to the start/end of the line." msgstr "" "Με αυτή την επιλογή ενεργοποιημένη, τα πλήκτρα Home/End μας μετακινούν στον " "πρώτο/τελευταίο χαρακτήρα πριν μεταβούμε στην αρχή/τέλος της γραμμής." #: ../code/dialogs/preferences-dialog.cpp:525 msgid "Text Wrapping" msgstr "Αναδίπλωση κειμένου" #: ../code/dialogs/preferences-dialog.cpp:526 msgid "Line Numbers" msgstr "Αριθμοί γραμμών" #: ../code/dialogs/preferences-dialog.cpp:527 msgid "Current Line" msgstr "Τρέχουσα γραμμή" #: ../code/dialogs/preferences-dialog.cpp:528 msgid "Right Margin" msgstr "Δεξί περιθώριο" #: ../code/dialogs/preferences-dialog.cpp:529 msgid "Bracket Matching" msgstr "Αγκύλες ταιριάσματος" #: ../code/dialogs/preferences-dialog.cpp:530 msgid "Whitespace Display" msgstr "Εμφάνιση λευκού διαστήματος" #: ../code/dialogs/preferences-dialog.cpp:531 msgid "Enable text wrapping" msgstr "Ενεργοποίηση αναδίπλωσης κειμένου" #: ../code/dialogs/preferences-dialog.cpp:532 msgid "Do not split words over two lines" msgstr "Να μη διαχωρίζονται οι λέξεις σε περισσότερες από δυο γραμμές" #: ../code/dialogs/preferences-dialog.cpp:533 msgid "Display line numbers" msgstr "Εμφάνιση αριθμών στις γραμμές" #: ../code/dialogs/preferences-dialog.cpp:534 msgid "Highlight current line" msgstr "Επισήμανση τρέχουσας γραμμής" #: ../code/dialogs/preferences-dialog.cpp:535 msgid "Display right margin" msgstr "Εμφάνιση δεξιού περιθωρίου" #: ../code/dialogs/preferences-dialog.cpp:536 msgid "Right margin at column:" msgstr "Δεξί περιθώριο σε στήλη:" #: ../code/dialogs/preferences-dialog.cpp:537 msgid "Highlight matching bracket" msgstr "Επισήμανση αγκύλης ταιριάσματος" #: ../code/dialogs/preferences-dialog.cpp:589 msgid "Display no whitespace" msgstr "Απόκρυψη λευκού διαστήματος" #: ../code/dialogs/preferences-dialog.cpp:592 msgid "Display spaces" msgstr "Εμφάνιση διαστημάτων" #: ../code/dialogs/preferences-dialog.cpp:596 msgid "Display tabs" msgstr "Εμφάνιση στηλοθετών" #: ../code/dialogs/preferences-dialog.cpp:600 msgid "Display tabs and spaces" msgstr "Εμφάνιση στηλοθετών και διαστημάτων" #: ../code/dialogs/preferences-dialog.cpp:651 msgid "Toolbar" msgstr "Γραμμή εργαλείων" #: ../code/dialogs/preferences-dialog.cpp:652 msgid "Font" msgstr "Γραμματοσειρά" #: ../code/dialogs/preferences-dialog.cpp:653 msgid "Color Scheme" msgstr "Απεικόνιση χρώματος" #: ../code/dialogs/preferences-dialog.cpp:661 msgid "Show text only" msgstr "Εμφάνιση μόνο του κειμένου" #: ../code/dialogs/preferences-dialog.cpp:663 msgid "Show icons only" msgstr "Εμφάνιση μόνο των εικονιδίων" #: ../code/dialogs/preferences-dialog.cpp:665 msgid "Show both icons and text" msgstr "Εμφάνιση εικονιδίων και κειμένου" #: ../code/dialogs/preferences-dialog.cpp:667 msgid "Show text besides icons" msgstr "Εμφάνιση κειμένου δίπλα στα εικονίδια" #: ../code/dialogs/preferences-dialog.cpp:756 msgid "Trusted CAs" msgstr "Έμπιστα CAs" #: ../code/dialogs/preferences-dialog.cpp:757 msgid "Secure Connection" msgstr "Ασφαλής σύνδεση" #: ../code/dialogs/preferences-dialog.cpp:758 #, fuzzy msgid "Authentication" msgstr "Εσοχή" #: ../code/dialogs/preferences-dialog.cpp:759 msgid "Select a file containing trusted CAs" msgstr "Επιλογή αρχείου που περιέχει έμπιστα CAs" #: ../code/dialogs/preferences-dialog.cpp:764 #, fuzzy msgid "None" msgstr "Κανένα" #: ../code/dialogs/preferences-dialog.cpp:765 msgid "Use a certificate" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:767 msgid "Select a private key file" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:769 #: ../code/dialogs/preferences-dialog.cpp:776 #, fuzzy msgid "Create New..." msgstr "Δημιουργία εγγράφου..." #: ../code/dialogs/preferences-dialog.cpp:773 msgid "Select a certificate file" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:801 msgid "Use TLS if possible" msgstr "Χρήση TLS εάν είναι δυνατό" #: ../code/dialogs/preferences-dialog.cpp:804 msgid "Always use TLS" msgstr "Παντοτινή χρήση TLS" #: ../code/dialogs/preferences-dialog.cpp:892 msgid "Error reading file:" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:916 msgid "2048-bit RSA private key is being generated, please wait..." msgstr "" #: ../code/dialogs/preferences-dialog.cpp:948 msgid "Select a location for the generated key" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:966 msgid "Select a location for the generated certificate" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:1068 msgid "Preferences" msgstr "Προτιμήσεις" #: ../code/dialogs/preferences-dialog.cpp:1073 msgid "User" msgstr "Χρήστης" #: ../code/dialogs/preferences-dialog.cpp:1074 msgid "Editor" msgstr "Επεξεργαστής" #: ../code/dialogs/preferences-dialog.cpp:1075 msgid "View" msgstr "Προβολή" #: ../code/dialogs/preferences-dialog.cpp:1076 msgid "Appearance" msgstr "Εμφάνιση" #: ../code/dialogs/preferences-dialog.cpp:1077 msgid "Security" msgstr "Ασφάλεια" #: ../code/main.cpp:194 msgid "Gobby options" msgstr "Επιλογές του Gobby" #: ../code/main.cpp:194 msgid "Options related to Gobby" msgstr "Επιλογές σχετικές με το Gobby" #: ../code/main.cpp:199 msgid "Display version information and exit" msgstr "Εμφάνιση πληροφοριών έκδοσης και έξοδος" #: ../code/main.cpp:206 msgid "Also start a new Gobby instance when there is one running already" msgstr "" "Έναρξη ενός νέου στιγμιότυπου Gobby όταν υπάρχει ένα που ήδη εκτελείται." #: ../code/main.cpp:214 msgid "Connect to given host on startup, can be given multiple times" msgstr "" "Η σύνδεση με τον συγκεκριμένο διακομιστή κατά την έναρξη, μπορεί να δοθεί " "πολλές φορές." #: ../code/main.cpp:215 msgid "HOSTNAME" msgstr "Όνομα διακομιστή" #: ../code/operations/operation-delete.cpp:65 msgid "Removing node \"%1\"..." msgstr "Αφαίρεση διακομιστή %1..." #: ../code/operations/operation-delete.cpp:75 msgid "Failed to delete node \"%1\"" msgstr "Αποτυχία διαγραφής διακομιστή %1: %2" #: ../code/operations/operation-export-html.cpp:168 #, c-format msgid "written by: %s" msgstr "συντάχθηκε από τον/την: %s" #: ../code/operations/operation-export-html.cpp:241 msgid "" msgstr "<αδυναμία εκτύπωσης της ημερομηνίας>" #. %1$s is session name/hostname #. %2$s is path within the session #. %3$s is current date as formatted by %c, #. %4$s is a link to the gobby site, it must be present because #. we need to handle that manually to insert a hyperlink #. instead of just printf'ing it. #: ../code/operations/operation-export-html.cpp:254 #, c-format msgid "Document generated from %1$s:%2$s at %3$s by %4$s" msgstr "Έγγραφο που δημιουργήθηκε από τους %1$s:%2$s at %3$s by %4$s" #: ../code/operations/operation-export-html.cpp:395 msgid "Participants" msgstr "Συμμετέχοντες" #: ../code/operations/operation-export-html.cpp:475 msgid "Exporting document \"%1\" to \"%2\" in HTML..." msgstr "Εξαγωγή εγγράφου \"%1\" σε \"%2\" σε HTML..." #: ../code/operations/operation-export-html.cpp:536 msgid "Failed to export document \"%1\" to HTML" msgstr "Αποτυχία εξαγωγής του εγγράφου \"%1\" σε HTML" #: ../code/operations/operation-new.cpp:75 msgid "Creating directory \"%1\"..." msgstr "Δημιουργία φακέλου \"%1\"..." #: ../code/operations/operation-new.cpp:76 msgid "Creating document \"%1\"..." msgstr "Δημιουργία εγγράφου \"%1\"..." #: ../code/operations/operation-new.cpp:88 msgid "Failed to create directory \"%1\"" msgstr "Αποτυχία δημιουργίας φακέλου \"%1\"" #: ../code/operations/operation-new.cpp:89 msgid "Failed to create document \"%1\"" msgstr "Αποτυχία δημιουργίας εγγράφου \"%1\"" #. Translators: This is the 8 bit encoding that is tried when #. autodetecting a file's encoding. #: ../code/operations/operation-open.cpp:38 msgid "ISO-8859-1" msgstr "ISO-8859-1" #: ../code/operations/operation-open.cpp:114 msgid "Opening document \"%1\"..." msgstr "Άνοιγμα εγγράφου \"%1\"..." #: ../code/operations/operation-open.cpp:134 msgid "" "The directory into which the new document was supposed to be inserted has " "been removed" msgstr "" "Ο φάκελος στον οποίο υποτίθεται ότι θα αποθηκευόταν το νέο έγγραφο έχει " "αφαιρεθεί." #: ../code/operations/operation-open.cpp:323 msgid "The file contains data not in the specified encoding" msgstr "" "Το αρχείο περιέχει δεδομένα που δεν διαθέτουν την καθορισμένη κωδικοποίηση." #: ../code/operations/operation-open.cpp:334 msgid "" "The file either contains data in an unknown encoding, or it contains binary " "data." msgstr "" "Είτε το αρχείο περιέχει δεδομένα σε άγνωστη κωδικοποίηση, είτε περιέχει " "δυαδικά δεδομένα." #: ../code/operations/operation-open-multiple.cpp:91 msgid "Parent folder was removed" msgstr "Ο γονικός φάκελος αφαιρέθηκε" #: ../code/operations/operation-open-multiple.cpp:193 msgid "Failed to open multiple documents" msgstr "Αποτυχία ανοίγματος πολλαπλών εγγράφων" #: ../code/operations/operation-save.cpp:97 msgid "Saving document \"%1\" to \"%2\"..." msgstr "Αποθήκευση εγγράφου \"%1\" στο \"%2\"..." #: ../code/operations/operation-save.cpp:226 msgid "" "The document contains one or more characters that cannot be encoded in the " "specified character coding." msgstr "" "Το έγγραφο περιέχει έναν ή περισσότερους χαρακτήρες που δεν μπορούν να " "κωδικοποιηθούν στην καθορισμένη κωδικοποίηση χαρακτήρων." #: ../code/operations/operation-save.cpp:287 msgid "Failed to save document \"%1\"" msgstr "Αποτυχία αποθήκευσης εγγράφου \"%1\"" #: ../code/operations/operation-subscribe-path.cpp:33 msgid "Invalid path: \"%1\"" msgstr "" #: ../code/operations/operation-subscribe-path.cpp:45 #, fuzzy msgid "Invalid path component: \"%1\"" msgstr "Αποτυχία ανοίγματος του εγγράφου \"%1\"" #: ../code/operations/operation-subscribe-path.cpp:135 #: ../code/operations/operation-subscribe-path.cpp:200 #, fuzzy msgid "Subscribing to \"%1\"..." msgstr "Εγγραφή στο %1..." #: ../code/operations/operation-subscribe-path.cpp:149 #: ../code/operations/operation-subscribe-path.cpp:212 #, fuzzy msgid "Failed to connect to \"%1\"" msgstr "Αποτυχία δημιουργίας φακέλου \"%1\"" #: ../code/operations/operation-subscribe-path.cpp:164 msgid "URI scheme \"%1\" not supported" msgstr "" #: ../code/operations/operation-subscribe-path.cpp:193 #, fuzzy msgid "Connecting to \"%1\"..." msgstr "Η σύνδεση στο \"%1\" απέτυχε" #: ../code/operations/operation-subscribe-path.cpp:220 #: ../code/operations/operation-subscribe-path.cpp:319 #: ../code/operations/operation-subscribe-path.cpp:356 #: ../code/operations/operation-subscribe-path.cpp:452 #: ../code/operations/operation-subscribe-path.cpp:472 #, fuzzy msgid "Could not subscribe to \"%1\"" msgstr "Αδυναμία επίλυσης \"%1\"" #: ../code/operations/operation-subscribe-path.cpp:322 #: ../code/operations/operation-subscribe-path.cpp:358 #, fuzzy msgid "Path \"%1\" does not exist" msgstr "Η συσκευή \"%1\" δεν υπάρχει" #: ../code/util/file.cpp:65 ../code/util/file.cpp:74 msgid "Could not create directory \"%1\": %2" msgstr "Αδυναμία δημιουργίας φακέλου \"%1\": %2" #: ../code/util/uri.cpp:91 msgid "Device \"%1\" does not exist" msgstr "Η συσκευή \"%1\" δεν υπάρχει" #: ../code/window.cpp:104 msgid "This Computer" msgstr "" #: ../code/window.cpp:129 msgid "Document Browser" msgstr "Περιηγητής εγγράφου" #: ../code/window.cpp:182 msgid "You are still connected to a session" msgstr "Είστε ακόμη συνδεδεμένοι σε μια συνεδρία" #: ../code/window.cpp:190 msgid "Do you want to close Gobby nevertheless?" msgstr "Θέλετε παρόλα αυτά να κλείσετε το Gobby;" #: ../code/window.cpp:196 msgid "C_ancel" msgstr "Άκυρο" #: ../gobby-0.5.desktop.in.h:1 msgid "Gobby" msgstr "Gobby" #: ../gobby-0.5.desktop.in.h:2 msgid "Collaborative Editor" msgstr "Συνεργατικός επεξεργαστής κειμένων" #: ../gobby-0.5.desktop.in.h:3 msgid "Gobby Collaborative Editor (0.5)" msgstr "Συνεργατικός επεξεργαστής κειμένων Gobby (0.5)" #: ../gobby-0.5.desktop.in.h:4 msgid "Edit text files collaboratively" msgstr "Συνεργατική επεξεργασία αρχείων κειμένου" #~ msgid "" #~ "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.\n" #~ "\n" #~ "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.\n" #~ "\n" #~ "You should have received a copy of the GNU General Public License along " #~ "with this program; if not, write to the Free Software Foundation, Inc., " #~ "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA" #~ msgstr "" #~ "Αυτή η εφαρμογή είναι Ελεύθερο Λογισμικό. Μπορείτε να την αναδιανείμετε ή " #~ "να την τροποποιήσετε υπό τους όρους της Γενικής Δημόσιας Άδειας GNU, όπως " #~ "έχει δημοσιευτεί από το Ίδρυμα Ελεύθερου Λογισμικού (FSF), είτε την " #~ "έκδοση 2, είτε (ανάλογα με τις προθέσεις σας) με κάθε επόμενη έκδοση.\n" #~ "\n" #~ "Αυτή η εφαρμογή διανέμεται με την ελπίδα ότι θα είναι χρήσιμη, αλλά ΧΩΡΙΣ " #~ "ΚΑΜΙΑ ΕΓΓΥΗΣΗ, χωρίς ακόμη υποδηλούμενη εγγύηση ΕΜΠΟΡΕΥΣΙΜΟΤΗΤΑΣ ή " #~ "ΚΑΤΑΛΛΗΛΟΤΗΤΑΣ ΓΙΑ ΣΥΓΚΕΚΡΙΜΕΝΟΥΣ ΣΚΟΠΟΥΣ. Δείτε τη Γενική Δημόσια Άδεια " #~ "GNU για περισσότερες λεπτομέρειες.\n" #~ "\n" #~ "Θα πρέπει να έχετε λάβει ένα αντίγραφο της άδειας αυτής μαζί με την " #~ "εφαρμογή. Εάν όχι, στείλτε μια επιστολή στο Ίδρυμα Ελεύθερου Λογισμικού " #~ "(FSF), Inc., 675 Mass Ave, Cambridge, MA 02139, USA." #~ msgid "User Join in progress..." #~ msgstr "Σύνδεση του χρήστη σε εξέλιξη..." #, fuzzy #~ msgid "Failed to resolve \"%1\": %2" #~ msgstr "Αδυναμία επίλυσης \"%1\"" #~ msgid "Resolving \"%1\"..." #~ msgstr "Επίλυση \"%1\"..." #~ msgid "Paths" #~ msgstr "Διαδρομές" #~ msgid "" #~ "The directory into which locally hosted sessions are permanently stored" #~ msgstr "" #~ "Ο φάκελος στον οποίο οι τοπικά φιλοξενούμενες συνεδρίες έχουν μόνιμα " #~ "αποθηκευτεί." #~ msgid "Subscription already in progress" #~ msgstr "Εγγραφή ήδη σε εξέλιξη" po/en_GB.po000066400000000000000000001115341304552132100130070ustar00rootroot00000000000000# British English translation for Gobby, the collaborative real-time text # editor. # Copyright (C) 2009 Gabríel A. Pétursson # This file is distributed under the same license as the Gobby package. # Gabríel A. Pétursson , 2009. # msgid "" msgstr "" "Project-Id-Version: gobby-0.4.93~dev\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2014-08-23 13:43-0400\n" "PO-Revision-Date: 2009-12-03 19:26-0000\n" "Last-Translator: Gabríel A. Pétursson \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n!=1;\n" #: ../code/commands/auth-commands.cpp:196 #, fuzzy msgid "Password required" msgstr "Password Required" #: ../code/commands/auth-commands.cpp:214 msgid "Incorrect password" msgstr "" #: ../code/commands/browser-commands.cpp:288 msgid "Subscribing to %1..." msgstr "Subscribing to %1..." #: ../code/commands/browser-commands.cpp:301 msgid "Subscribing to chat on %1..." msgstr "Subscribing to chat on %1..." #: ../code/commands/browser-commands.cpp:591 msgid "Subscription failed" msgstr "Subscription failed" #: ../code/commands/browser-context-commands.cpp:86 msgid "Could not find a location where to store the certificate" msgstr "" #: ../code/commands/browser-context-commands.cpp:153 msgid "_Remove" msgstr "" #: ../code/commands/browser-context-commands.cpp:188 #, fuzzy msgid "_Disconnect from Server" msgstr "Connect to Server" #: ../code/commands/browser-context-commands.cpp:220 #, fuzzy msgid "Create _Account..." msgstr "Create Do_cument..." #: ../code/commands/browser-context-commands.cpp:245 msgid "Create Do_cument..." msgstr "Create Do_cument..." #: ../code/commands/browser-context-commands.cpp:280 #, fuzzy msgid "Create Di_rectory..." msgstr "Create Directory..." #: ../code/commands/browser-context-commands.cpp:293 msgid "_Open Document..." msgstr "_Open Document..." #: ../code/commands/browser-context-commands.cpp:312 msgid "_Permissions..." msgstr "" #: ../code/commands/browser-context-commands.cpp:324 msgid "D_elete" msgstr "D_elete" #: ../code/commands/browser-context-commands.cpp:405 msgid "Choose a name for the directory" msgstr "Choose a name for the directory" #: ../code/commands/browser-context-commands.cpp:406 msgid "Choose a name for the document" msgstr "Choose a name for the document" #: ../code/commands/browser-context-commands.cpp:407 msgid "_Directory Name:" msgstr "_Directory Name:" #: ../code/commands/browser-context-commands.cpp:408 msgid "_Document Name:" msgstr "_Document Name:" #: ../code/commands/browser-context-commands.cpp:411 msgid "C_reate" msgstr "C_reate" #: ../code/commands/browser-context-commands.cpp:415 msgid "New Directory" msgstr "New Directory" #: ../code/commands/browser-context-commands.cpp:416 #: ../code/commands/file-tasks/task-new.cpp:36 msgid "New Document" msgstr "New Document" #: ../code/commands/browser-context-commands.cpp:436 #: ../code/commands/file-tasks/task-open-file.cpp:22 msgid "Choose a text file to open" msgstr "Choose a text file to open" #: ../code/commands/browser-context-commands.cpp:558 #: ../code/commands/browser-context-commands.cpp:569 msgid "Account successfully created" msgstr "" #: ../code/commands/browser-context-commands.cpp:562 msgid "When re-connecting to the server, the new account will be used." msgstr "" #: ../code/commands/browser-context-commands.cpp:573 msgid "" "The certificate has been stored at %1.\n" "\n" "To login to this account, set the certificate in Gobby's preferences and re-" "connect to the server." msgstr "" #: ../code/commands/browser-context-commands.cpp:586 #, fuzzy msgid "Failed to create account" msgstr "Failed to create document \"%1\"" #: ../code/commands/browser-context-commands.cpp:589 msgid "Could not save the certificate for the account: %1" msgstr "" #: ../code/commands/file-tasks/task-export-html.cpp:27 msgid "Choose a location to export document \"%1\" to" msgstr "Choose a location to export document \"%1\" to" #: ../code/commands/file-tasks/task-open.cpp:46 msgid "Querying \"%1\"..." msgstr "Querying \"%1\"..." #: ../code/commands/file-tasks/task-open.cpp:103 #: ../code/operations/operation-open.cpp:474 #: ../code/operations/operation-open-multiple.cpp:180 msgid "Failed to open document \"%1\"" msgstr "Failed to open document \"%1\"" #: ../code/commands/file-tasks/task-save.cpp:24 msgid "Choose a location to save document \"%1\" to" msgstr "Choose a location to save document \"%1\" to" #. Help browser could not be invoked, show an error message to the user. #: ../code/commands/help-commands.cpp:70 msgid "There was an error displaying help." msgstr "There was an error displaying help." #: ../code/commands/help-commands.cpp:93 msgid "Contributors:" msgstr "Contributors:" #: ../code/commands/help-commands.cpp:99 msgid "British English:" msgstr "British English:" #: ../code/commands/help-commands.cpp:101 msgid "German:" msgstr "German:" #: ../code/commands/help-commands.cpp:118 msgid "" "Permission to use, copy, modify, and/or distribute this software for any " "urpose with or without fee is hereby granted, provided that the above " "copyright notice and this permission notice appear in all copies.\n" "\n" "THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES " "WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF " "MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY " "SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES " "WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION " "OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN " "CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE." msgstr "" #: ../code/commands/subscription-commands.cpp:148 msgid "" "The connection to the publisher of this document has been lost. Further " "changes to the document could not be synchronized to others anymore, " "therefore the document cannot be edited anymore.\n" "\n" "Please note also that it is possible that not all of your latest changes " "have reached the publisher before the connection was lost." msgstr "" "The connection to the publisher of this document has been lost. Further " "changes to the document could not be synchronised to others any longer, " "therefore the document cannot be edited any longer.\n" "\n" "Please note also that it is possible that not all of your latest changes " "have reached the publisher before the connection was lost." #: ../code/commands/synchronization-commands.cpp:30 msgid "Synchronization in progress... %1%%" msgstr "Synchronisation in progress... %1%%" #. Document cannot be used if an error happened #. during synchronization. #: ../code/commands/synchronization-commands.cpp:42 msgid "This document cannot be used." msgstr "This document cannot be used." #: ../code/commands/synchronization-commands.cpp:45 #: ../code/commands/user-join-commands.cpp:43 msgid "" "If you have an idea what could have caused the problem, then you may attempt " "to solve it and try again (after having closed this document). Otherwise it " "is most likely a bug in the software. In that case, please file a bug report " "at http://gobby.0x539.de/trac/newticket and provide as much information as " "you can, including what you did when the problem occurred and how to " "reproduce the problem (if possible) so that we can fix the problem in a " "later version. Thank you." msgstr "" "If you have an idea what could have caused the problem, then you may attempt " "to solve it and try again (after having closed this document). Otherwise it " "is most likely a bug in the software. In that case, please file a bug report " "at http://gobby.0x539.de/trac/newticket and provide as much information as " "you can, including what you did when the problem occurred and how to " "reproduce the problem (if possible) in order for us to fix the problem in a " "later version. Thank you." #: ../code/commands/synchronization-commands.cpp:59 msgid "Synchronization failed: %1" msgstr "Synchronisation failed: %1" #. TODO: Adjust this for chat sessions #: ../code/commands/user-join-commands.cpp:38 msgid "" "You can still watch others editing the document, but you cannot edit it " "yourself." msgstr "" "You can still watch others editing the document, but you cannot edit it " "yourself." #: ../code/commands/user-join-commands.cpp:57 msgid "User Join failed: %1" msgstr "User Join failed: %1" #: ../code/commands/user-join-commands.cpp:64 msgid "Permissions are not granted to modify the document." msgstr "" #: ../code/core/browser.cpp:111 msgid "_Direct Connection" msgstr "_Direct Connection" #: ../code/core/browser.cpp:113 msgid "Host Name:" msgstr "Host Name:" #: ../code/core/certificatemanager.cpp:83 #, c-format msgid "Failed to write Diffie-Hellman parameters to \"%s\": %s" msgstr "" #: ../code/core/certificatemanager.cpp:276 #, c-format msgid "Failed to read Diffie-Hellman parameters: %s" msgstr "" #: ../code/core/certificatemanager.cpp:338 msgid "File does not contain a X.509 certificate" msgstr "" #: ../code/core/certificatemanager.cpp:414 msgid "Certificate does not belong to the chosen key" msgstr "" #: ../code/core/foldermanager.cpp:322 msgid "The document has been removed from the server." msgstr "" #: ../code/core/header.cpp:203 msgid "_None" msgstr "_None" #: ../code/core/header.cpp:227 msgid "_File" msgstr "_File" #: ../code/core/header.cpp:230 msgid "_Open..." msgstr "_Open..." #: ../code/core/header.cpp:233 msgid "Open _Location..." msgstr "Open _Location..." #: ../code/core/header.cpp:237 msgid "Save _As..." msgstr "Save _As..." #: ../code/core/header.cpp:241 ../code/core/iconmanager.cpp:43 msgid "Save All" msgstr "Save All" #: ../code/core/header.cpp:241 msgid "Save all open files locally" msgstr "Save all open files locally" #: ../code/core/header.cpp:243 msgid "Export As _HTML..." msgstr "Export As _HTML..." #: ../code/core/header.cpp:246 msgid "Connect _to Server..." msgstr "Connect _to Server..." #: ../code/core/header.cpp:251 msgid "_Edit" msgstr "_Edit" #: ../code/core/header.cpp:259 msgid "_Find..." msgstr "_Find..." #: ../code/core/header.cpp:261 msgid "Find Ne_xt" msgstr "Find Ne_xt" #: ../code/core/header.cpp:262 msgid "Find next match of phrase searched for" msgstr "Find next match of phrase searched for" #: ../code/core/header.cpp:265 msgid "Find Pre_vious" msgstr "Find Pre_vious" #: ../code/core/header.cpp:266 msgid "Find previous match of phrase searched for" msgstr "Find previous match of phrase searched for" #: ../code/core/header.cpp:271 msgid "Find and Rep_lace..." msgstr "Find and Rep_lace..." #: ../code/core/header.cpp:275 msgid "Go to _Line..." msgstr "Go to _Line..." #: ../code/core/header.cpp:279 msgid "Pr_eferences..." msgstr "Pr_eferences..." #: ../code/core/header.cpp:281 msgid "_View" msgstr "_View" #: ../code/core/header.cpp:284 msgid "Reset User Colors" msgstr "Reset User Colours" #: ../code/core/header.cpp:285 msgid "Hide user colors in current document" msgstr "Hide user colours in current document" #: ../code/core/header.cpp:289 msgid "Fullscreen" msgstr "" #: ../code/core/header.cpp:290 msgid "Show the editing window in fullscreen mode" msgstr "" #: ../code/core/header.cpp:294 msgid "Zoom In" msgstr "" #: ../code/core/header.cpp:297 msgid "Zoom Out" msgstr "" #: ../code/core/header.cpp:300 msgid "View Toolbar" msgstr "View Toolbar" #: ../code/core/header.cpp:301 msgid "Whether to show the toolbar" msgstr "Whether to show the toolbar" #: ../code/core/header.cpp:305 msgid "View Statusbar" msgstr "View Statusbar" #: ../code/core/header.cpp:306 msgid "Whether to show the statusbar" msgstr "Whether to show the statusbar" #: ../code/core/header.cpp:310 msgid "View Document Browser" msgstr "View Document Browser" #: ../code/core/header.cpp:311 msgid "Whether to show the document browser" msgstr "Whether to show the document browser" #: ../code/core/header.cpp:315 msgid "View Chat" msgstr "View Chat" #: ../code/core/header.cpp:316 msgid "Whether to show the chat pane" msgstr "Whether to show the chat pane" #: ../code/core/header.cpp:320 msgid "View Document User List" msgstr "View Document User List" #: ../code/core/header.cpp:321 msgid "Whether to show the user list for documents" msgstr "Whether to show the user list for documents" #: ../code/core/header.cpp:325 msgid "View Chat User List" msgstr "View Chat User List" #: ../code/core/header.cpp:326 msgid "Whether to show the user list for the chat" msgstr "Whether to show the user list for the chat" #: ../code/core/header.cpp:330 msgid "_Highlight Mode" msgstr "_Highlight Mode" #: ../code/core/header.cpp:336 msgid "_Help" msgstr "_Help" #: ../code/core/header.cpp:339 msgid "_Contents" msgstr "_Contents" #: ../code/core/header.cpp:340 msgid "Opens the Gobby manual" msgstr "Opens the Gobby manual" #: ../code/core/header.cpp:343 msgid "_About..." msgstr "_About..." #: ../code/core/header.cpp:344 msgid "Shows Gobby's copyright and credits" msgstr "Shows Gobby's copyright and credits" #: ../code/core/iconmanager.cpp:49 msgid "User list" msgstr "User list" #: ../code/core/iconmanager.cpp:55 msgid "Document list" msgstr "Document list" #: ../code/core/iconmanager.cpp:61 ../code/window.cpp:52 msgid "Chat" msgstr "Chat" #: ../code/core/iconmanager.cpp:68 msgid "User Color Indicator" msgstr "User Colour Indicator" #: ../code/core/selfhoster.cpp:103 msgid "Generating 2048-bit Diffie-Hellman parameters..." msgstr "" #: ../code/core/selfhoster.cpp:135 #, fuzzy msgid "Failed to generate Diffie-Hellman parameters" msgstr "Failed to delete node \"%1\"" #: ../code/core/selfhoster.cpp:137 msgid "" "This means that Perfect Forward Secrecy (PFS) is not available. Restart " "Gobby to re-try generating the parameters. The specific error was:\n" "\n" "%1" msgstr "" #: ../code/core/selfhoster.cpp:262 msgid "" "In order to start sharing your documents, choose a private key and " "certificate or create a new pair in the preferences" msgstr "" #: ../code/core/selfhoster.cpp:282 #, fuzzy msgid "Failed to share documents" msgstr "Failed to save document \"%1\"" #: ../code/core/sessionuserview.cpp:39 msgid "User List" msgstr "User List" #: ../code/core/statusbar.cpp:418 msgid "Ln %1, Col %2\t%3" msgstr "Ln %1, Col %2\t%3" #: ../code/core/statusbar.cpp:421 msgid "OVR" msgstr "OVR" #: ../code/core/statusbar.cpp:421 msgid "INS" msgstr "INS" #: ../code/core/textsessionview.cpp:638 msgid "Text written by %1" msgstr "Text written by %1" #: ../code/core/textsessionview.cpp:644 msgid "Unowned text" msgstr "Ownerless text" #: ../code/core/userlist.cpp:108 msgid "Users" msgstr "Users" #: ../code/dialogs/connection-dialog.cpp:26 msgid "Connect to Server" msgstr "Connect to Server" #: ../code/dialogs/connection-dialog.cpp:30 msgid "Please enter a host name with which to establish a connection." msgstr "Please enter a host name with which to establish a connection." #: ../code/dialogs/connection-dialog.cpp:32 msgid "_Remote Endpoint:" msgstr "_Remote Endpoint:" #: ../code/dialogs/document-location-dialog.cpp:28 msgid "Document Name:" msgstr "Document Name:" #: ../code/dialogs/document-location-dialog.cpp:155 msgid "Select document's target location" msgstr "Select document's target location" #: ../code/dialogs/document-location-dialog.cpp:157 msgid "Choose a directory to create the document into:" msgstr "Choose a directory to create the document into:" #: ../code/dialogs/document-location-dialog.cpp:163 msgid "Select documents' target location" msgstr "Select documents' target location" #: ../code/dialogs/document-location-dialog.cpp:165 msgid "Choose a directory to create the documents into:" msgstr "Choose a directory to create the documents into:" #: ../code/dialogs/find-dialog.cpp:44 ../code/dialogs/find-dialog.cpp:142 msgid "Find" msgstr "Find" #: ../code/dialogs/find-dialog.cpp:47 msgid "_Search for:" msgstr "_Search for:" #: ../code/dialogs/find-dialog.cpp:49 msgid "Replace _with:" msgstr "Replace _with:" #: ../code/dialogs/find-dialog.cpp:51 msgid "_Match case" msgstr "_Match case" #: ../code/dialogs/find-dialog.cpp:52 msgid "Match _entire word only" msgstr "Match _entire word only" #: ../code/dialogs/find-dialog.cpp:53 msgid "Search _backwards" msgstr "Search _backwards" #: ../code/dialogs/find-dialog.cpp:54 msgid "Wra_p around" msgstr "Wra_p around" #: ../code/dialogs/find-dialog.cpp:100 msgid "Replace _All" msgstr "Replace _All" #: ../code/dialogs/find-dialog.cpp:102 msgid "_Replace" msgstr "_Replace" #: ../code/dialogs/find-dialog.cpp:142 msgid "Replace" msgstr "Replace" #: ../code/dialogs/find-dialog.cpp:161 ../code/dialogs/find-dialog.cpp:177 msgid "Phrase \"%1\" has not been found" msgstr "Phrase \"%1\" has not been found" #: ../code/dialogs/find-dialog.cpp:335 msgid "No occurrence has been replaced" msgstr "No occurrence has been replaced" #: ../code/dialogs/find-dialog.cpp:341 msgid "%1 occurrence has been replaced" msgid_plural "%1 occurrences have been replaced" msgstr[0] "%1 occurrence has been replaced" msgstr[1] "%1 occurrences have been replaced" #: ../code/dialogs/goto-dialog.cpp:26 msgid "Go to line" msgstr "Go to line" #: ../code/dialogs/goto-dialog.cpp:29 msgid "Line _number:" msgstr "Line _number:" #: ../code/dialogs/goto-dialog.cpp:50 msgid "Go To _Line" msgstr "Go To _Line" #: ../code/dialogs/initial-dialog.cpp:76 msgid "Generating 2048-bit RSA private key..." msgstr "" #: ../code/dialogs/initial-dialog.cpp:99 #, fuzzy msgid "Failed to generate private key" msgstr "Failed to delete node \"%1\"" #: ../code/dialogs/initial-dialog.cpp:102 msgid "" "%1\n" "\n" "You can try again to create a key in the Security tab of the preferences " "dialog." msgstr "" #: ../code/dialogs/initial-dialog.cpp:141 msgid "Failed to generate self-signed certificate" msgstr "" #: ../code/dialogs/initial-dialog.cpp:144 msgid "" "%1\n" "\n" "You can try again to create a certificate in the Security tab of the " "preferences dialog." msgstr "" #: ../code/dialogs/initial-dialog.cpp:198 msgid "Choose a user color" msgstr "Choose a user colour" #: ../code/dialogs/initial-dialog.cpp:204 msgid "Welcome to Gobby" msgstr "Welcome to Gobby" #: ../code/dialogs/initial-dialog.cpp:216 msgid "" "Before we start, a few options need to be configured. You can later change " "them by choosing Edit ▸ Preferences from the menu." msgstr "" "Before we start, a few options need to be configured. You can later change " "them by choosing Edit ▸ Preferences from the menu." #: ../code/dialogs/initial-dialog.cpp:224 msgid "User Name" msgstr "User Name" #: ../code/dialogs/initial-dialog.cpp:226 msgid "Your name as shown to other users." msgstr "Your name as shown to other users." #: ../code/dialogs/initial-dialog.cpp:235 msgid "User Color" msgstr "User Colour" #: ../code/dialogs/initial-dialog.cpp:237 msgid "The color with which text you have written is branded." msgstr "The colour with which text you have written is branded." #: ../code/dialogs/initial-dialog.cpp:256 #, fuzzy msgid "Remote Connections" msgstr "Secure Connection" #: ../code/dialogs/initial-dialog.cpp:262 #: ../code/dialogs/preferences-dialog.cpp:217 msgid "Allow remote users to edit local documents" msgstr "" #: ../code/dialogs/initial-dialog.cpp:268 msgid "Ask remote users for a password" msgstr "" #: ../code/dialogs/initial-dialog.cpp:273 #: ../code/dialogs/preferences-dialog.cpp:220 #, fuzzy msgid "Password:" msgstr "Server _Password:" #: ../code/dialogs/initial-dialog.cpp:297 msgid "No authentication (Not recommended)" msgstr "" #: ../code/dialogs/initial-dialog.cpp:304 msgid "" "Don't authenticate ourselves to remote users. Data transfer will be " "unencrypted." msgstr "" #: ../code/dialogs/initial-dialog.cpp:313 msgid "Create a self-signed certificate (Recommended)" msgstr "" #: ../code/dialogs/initial-dialog.cpp:321 msgid "" "It may take a minute or two until remote users can connect while the " "security certificate is being generated." msgstr "" #: ../code/dialogs/initial-dialog.cpp:331 msgid "Use an existing certificate (Expert option)" msgstr "" #: ../code/dialogs/initial-dialog.cpp:338 msgid "" "Use an existing private key and certificate for authentication. The files " "must be in PEM format." msgstr "" #: ../code/dialogs/initial-dialog.cpp:347 #: ../code/dialogs/preferences-dialog.cpp:766 msgid "Private key:" msgstr "" #: ../code/dialogs/initial-dialog.cpp:349 #: ../code/dialogs/preferences-dialog.cpp:772 msgid "Certificate:" msgstr "" #: ../code/dialogs/open-location-dialog.cpp:26 msgid "Open Location" msgstr "Open Location" #: ../code/dialogs/open-location-dialog.cpp:27 msgid "Enter the _location (URI) of the file you would like to open:" msgstr "Enter the _location (URI) of the file you would like to open:" #: ../code/dialogs/password-dialog.cpp:25 msgid "Password Required" msgstr "Password Required" #: ../code/dialogs/password-dialog.cpp:31 msgid "Connection to host \"%1\" requires a password." msgstr "Connection to host \"%1\" requires a password." #: ../code/dialogs/password-dialog.cpp:32 msgid "Invalid password for host \"%1\". Please try again." msgstr "Invalid password for host \"%1\". Please try again." #: ../code/dialogs/password-dialog.cpp:35 msgid "Server _Password:" msgstr "Server _Password:" #: ../code/dialogs/preferences-dialog.cpp:198 msgid "Settings" msgstr "Settings" #: ../code/dialogs/preferences-dialog.cpp:199 #, fuzzy msgid "Remote Users" msgstr "Reset User Colours" #: ../code/dialogs/preferences-dialog.cpp:200 #, fuzzy msgid "Local Documents" msgstr "New Document" #: ../code/dialogs/preferences-dialog.cpp:202 msgid "User name:" msgstr "User name:" #: ../code/dialogs/preferences-dialog.cpp:204 msgid "User color:" msgstr "User colour:" #: ../code/dialogs/preferences-dialog.cpp:206 msgid "Color intensity:" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:208 msgid "Choose a new user color" msgstr "Choose a new user colour" #: ../code/dialogs/preferences-dialog.cpp:209 msgid "Show cursors of remote users" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:210 msgid "Show selections of remote users" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:212 #, fuzzy msgid "Highlight current line of remote users" msgstr "Highlight current line" #: ../code/dialogs/preferences-dialog.cpp:214 msgid "Indicate cursor position of remote users in the scrollbar" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:219 msgid "Require remote users to enter a password" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:222 msgid "Port:" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:226 msgid "Remember local documents after Gobby restart" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:227 #, fuzzy msgid "Local documents directory:" msgstr "Host directory:" #: ../code/dialogs/preferences-dialog.cpp:423 msgid "Tab Stops" msgstr "Tab Stops" #: ../code/dialogs/preferences-dialog.cpp:424 msgid "Indentation" msgstr "Indentation" #: ../code/dialogs/preferences-dialog.cpp:425 msgid "Home/End Behavior" msgstr "Home/End Behaviour" #: ../code/dialogs/preferences-dialog.cpp:426 msgid "File Saving" msgstr "File Saving" #: ../code/dialogs/preferences-dialog.cpp:427 msgid "_Tab width:" msgstr "_Tab width:" #: ../code/dialogs/preferences-dialog.cpp:429 msgid "Insert _spaces instead of tabs" msgstr "Insert _spaces instead of tabs" #: ../code/dialogs/preferences-dialog.cpp:430 msgid "Enable automatic _indentation" msgstr "Enable automatic _indentation" #: ../code/dialogs/preferences-dialog.cpp:431 msgid "Smart _home/end" msgstr "Smart _home/end" #: ../code/dialogs/preferences-dialog.cpp:432 msgid "Enable _automatic saving of documents" msgstr "Enable _automatic saving of documents" #: ../code/dialogs/preferences-dialog.cpp:434 msgid "Autosave interval in _minutes:" msgstr "Autosave interval in _minutes:" #: ../code/dialogs/preferences-dialog.cpp:455 msgid "" "With this option enabled, Home/End keys move to first/last character before " "going to the start/end of the line." msgstr "" "With this option enabled, Home/End keys move to first/last character before " "going to the start/end of the line." #: ../code/dialogs/preferences-dialog.cpp:525 msgid "Text Wrapping" msgstr "Text Wrapping" #: ../code/dialogs/preferences-dialog.cpp:526 msgid "Line Numbers" msgstr "Line Numbers" #: ../code/dialogs/preferences-dialog.cpp:527 msgid "Current Line" msgstr "Current Line" #: ../code/dialogs/preferences-dialog.cpp:528 msgid "Right Margin" msgstr "Right Margin" #: ../code/dialogs/preferences-dialog.cpp:529 msgid "Bracket Matching" msgstr "Bracket Matching" #: ../code/dialogs/preferences-dialog.cpp:530 msgid "Whitespace Display" msgstr "Whitespace Display" #: ../code/dialogs/preferences-dialog.cpp:531 msgid "Enable text wrapping" msgstr "Enable text wrapping" #: ../code/dialogs/preferences-dialog.cpp:532 msgid "Do not split words over two lines" msgstr "Do not split words over two lines" #: ../code/dialogs/preferences-dialog.cpp:533 msgid "Display line numbers" msgstr "Display line numbers" #: ../code/dialogs/preferences-dialog.cpp:534 msgid "Highlight current line" msgstr "Highlight current line" #: ../code/dialogs/preferences-dialog.cpp:535 msgid "Display right margin" msgstr "Display right margin" #: ../code/dialogs/preferences-dialog.cpp:536 msgid "Right margin at column:" msgstr "Right margin at column:" #: ../code/dialogs/preferences-dialog.cpp:537 msgid "Highlight matching bracket" msgstr "Highlight matching bracket" #: ../code/dialogs/preferences-dialog.cpp:589 msgid "Display no whitespace" msgstr "Display no whitespace" #: ../code/dialogs/preferences-dialog.cpp:592 msgid "Display spaces" msgstr "Display spaces" #: ../code/dialogs/preferences-dialog.cpp:596 msgid "Display tabs" msgstr "Display tabs" #: ../code/dialogs/preferences-dialog.cpp:600 msgid "Display tabs and spaces" msgstr "Display tabs and spaces" #: ../code/dialogs/preferences-dialog.cpp:651 msgid "Toolbar" msgstr "Toolbar" #: ../code/dialogs/preferences-dialog.cpp:652 msgid "Font" msgstr "Font" #: ../code/dialogs/preferences-dialog.cpp:653 msgid "Color Scheme" msgstr "Colour Scheme" #: ../code/dialogs/preferences-dialog.cpp:661 msgid "Show text only" msgstr "Show text only" #: ../code/dialogs/preferences-dialog.cpp:663 msgid "Show icons only" msgstr "Show icons only" #: ../code/dialogs/preferences-dialog.cpp:665 msgid "Show both icons and text" msgstr "Show both icons and text" #: ../code/dialogs/preferences-dialog.cpp:667 msgid "Show text besides icons" msgstr "Show text besides icons" #: ../code/dialogs/preferences-dialog.cpp:756 msgid "Trusted CAs" msgstr "Trusted CAs" #: ../code/dialogs/preferences-dialog.cpp:757 msgid "Secure Connection" msgstr "Secure Connection" #: ../code/dialogs/preferences-dialog.cpp:758 #, fuzzy msgid "Authentication" msgstr "Indentation" #: ../code/dialogs/preferences-dialog.cpp:759 msgid "Select a file containing trusted CAs" msgstr "Select a file containing trusted CAs" #: ../code/dialogs/preferences-dialog.cpp:764 #, fuzzy msgid "None" msgstr "_None" #: ../code/dialogs/preferences-dialog.cpp:765 msgid "Use a certificate" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:767 msgid "Select a private key file" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:769 #: ../code/dialogs/preferences-dialog.cpp:776 #, fuzzy msgid "Create New..." msgstr "Create Do_cument..." #: ../code/dialogs/preferences-dialog.cpp:773 msgid "Select a certificate file" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:801 msgid "Use TLS if possible" msgstr "Use TLS where possible" #: ../code/dialogs/preferences-dialog.cpp:804 msgid "Always use TLS" msgstr "Always use TLS" #: ../code/dialogs/preferences-dialog.cpp:892 msgid "Error reading file:" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:916 msgid "2048-bit RSA private key is being generated, please wait..." msgstr "" #: ../code/dialogs/preferences-dialog.cpp:948 msgid "Select a location for the generated key" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:966 msgid "Select a location for the generated certificate" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:1068 msgid "Preferences" msgstr "Preferences" #: ../code/dialogs/preferences-dialog.cpp:1073 msgid "User" msgstr "User" #: ../code/dialogs/preferences-dialog.cpp:1074 msgid "Editor" msgstr "Editor" #: ../code/dialogs/preferences-dialog.cpp:1075 msgid "View" msgstr "View" #: ../code/dialogs/preferences-dialog.cpp:1076 msgid "Appearance" msgstr "Appearance" #: ../code/dialogs/preferences-dialog.cpp:1077 msgid "Security" msgstr "Security" #: ../code/main.cpp:194 msgid "Gobby options" msgstr "Gobby options" #: ../code/main.cpp:194 msgid "Options related to Gobby" msgstr "Options related to Gobby" #: ../code/main.cpp:199 msgid "Display version information and exit" msgstr "Display version information and exit" #: ../code/main.cpp:206 msgid "Also start a new Gobby instance when there is one running already" msgstr "Also start a new Gobby instance when there is one running already" #: ../code/main.cpp:214 msgid "Connect to given host on startup, can be given multiple times" msgstr "Connect to given host on startup, can be given multiple times" #: ../code/main.cpp:215 msgid "HOSTNAME" msgstr "HOSTNAME" #: ../code/operations/operation-delete.cpp:65 msgid "Removing node \"%1\"..." msgstr "Removing node \"%1\"..." #: ../code/operations/operation-delete.cpp:75 msgid "Failed to delete node \"%1\"" msgstr "Failed to delete node \"%1\"" #: ../code/operations/operation-export-html.cpp:168 #, c-format msgid "written by: %s" msgstr "written by: %s" #: ../code/operations/operation-export-html.cpp:241 msgid "" msgstr "" #. %1$s is session name/hostname #. %2$s is path within the session #. %3$s is current date as formatted by %c, #. %4$s is a link to the gobby site, it must be present because #. we need to handle that manually to insert a hyperlink #. instead of just printf'ing it. #: ../code/operations/operation-export-html.cpp:254 #, c-format msgid "Document generated from %1$s:%2$s at %3$s by %4$s" msgstr "Document generated from %1$s:%2$s at %3$s by %4$s" #: ../code/operations/operation-export-html.cpp:395 msgid "Participants" msgstr "Participants" #: ../code/operations/operation-export-html.cpp:475 msgid "Exporting document \"%1\" to \"%2\" in HTML..." msgstr "Exporting document \"%1\" to \"%2\" in HTML..." #: ../code/operations/operation-export-html.cpp:536 msgid "Failed to export document \"%1\" to HTML" msgstr "Failed to export document \"%1\" to HTML" #: ../code/operations/operation-new.cpp:75 msgid "Creating directory \"%1\"..." msgstr "Creating directory \"%1\"..." #: ../code/operations/operation-new.cpp:76 msgid "Creating document \"%1\"..." msgstr "Creating document \"%1\"..." #: ../code/operations/operation-new.cpp:88 msgid "Failed to create directory \"%1\"" msgstr "Failed to create directory \"%1\"" #: ../code/operations/operation-new.cpp:89 msgid "Failed to create document \"%1\"" msgstr "Failed to create document \"%1\"" #. Translators: This is the 8 bit encoding that is tried when #. autodetecting a file's encoding. #: ../code/operations/operation-open.cpp:38 msgid "ISO-8859-1" msgstr "ISO-8859-1" #: ../code/operations/operation-open.cpp:114 msgid "Opening document \"%1\"..." msgstr "Opening document \"%1\"..." #: ../code/operations/operation-open.cpp:134 msgid "" "The directory into which the new document was supposed to be inserted has " "been removed" msgstr "" "The directory into which the new document was supposed to be inserted has " "been removed" #: ../code/operations/operation-open.cpp:323 msgid "The file contains data not in the specified encoding" msgstr "The file contains data not in the specified encoding" #: ../code/operations/operation-open.cpp:334 msgid "" "The file either contains data in an unknown encoding, or it contains binary " "data." msgstr "" "The file either contains data in an unknown encoding, or it contains binary " "data." #: ../code/operations/operation-open-multiple.cpp:91 msgid "Parent folder was removed" msgstr "Parent folder was removed" #: ../code/operations/operation-open-multiple.cpp:193 msgid "Failed to open multiple documents" msgstr "Failed to open multiple documents" #: ../code/operations/operation-save.cpp:97 msgid "Saving document \"%1\" to \"%2\"..." msgstr "Saving document \"%1\" to \"%2\"..." #: ../code/operations/operation-save.cpp:226 msgid "" "The document contains one or more characters that cannot be encoded in the " "specified character coding." msgstr "" "The document contains one or more characters that cannot be encoded in the " "specified character coding." #: ../code/operations/operation-save.cpp:287 msgid "Failed to save document \"%1\"" msgstr "Failed to save document \"%1\"" #: ../code/operations/operation-subscribe-path.cpp:33 msgid "Invalid path: \"%1\"" msgstr "" #: ../code/operations/operation-subscribe-path.cpp:45 #, fuzzy msgid "Invalid path component: \"%1\"" msgstr "Failed to open document \"%1\"" #: ../code/operations/operation-subscribe-path.cpp:135 #: ../code/operations/operation-subscribe-path.cpp:200 #, fuzzy msgid "Subscribing to \"%1\"..." msgstr "Subscribing to %1..." #: ../code/operations/operation-subscribe-path.cpp:149 #: ../code/operations/operation-subscribe-path.cpp:212 #, fuzzy msgid "Failed to connect to \"%1\"" msgstr "Failed to create directory \"%1\"" #: ../code/operations/operation-subscribe-path.cpp:164 msgid "URI scheme \"%1\" not supported" msgstr "" #: ../code/operations/operation-subscribe-path.cpp:193 #, fuzzy msgid "Connecting to \"%1\"..." msgstr "Connection to \"%1\" failed" #: ../code/operations/operation-subscribe-path.cpp:220 #: ../code/operations/operation-subscribe-path.cpp:319 #: ../code/operations/operation-subscribe-path.cpp:356 #: ../code/operations/operation-subscribe-path.cpp:452 #: ../code/operations/operation-subscribe-path.cpp:472 #, fuzzy msgid "Could not subscribe to \"%1\"" msgstr "Could not resolve \"%1\"" #: ../code/operations/operation-subscribe-path.cpp:322 #: ../code/operations/operation-subscribe-path.cpp:358 #, fuzzy msgid "Path \"%1\" does not exist" msgstr "Device \"%1\" does not exist" #: ../code/util/file.cpp:65 ../code/util/file.cpp:74 msgid "Could not create directory \"%1\": %2" msgstr "Could not create directory \"%1\": %2" #: ../code/util/uri.cpp:91 msgid "Device \"%1\" does not exist" msgstr "Device \"%1\" does not exist" #: ../code/window.cpp:104 msgid "This Computer" msgstr "" #: ../code/window.cpp:129 msgid "Document Browser" msgstr "Document Browser" #: ../code/window.cpp:182 msgid "You are still connected to a session" msgstr "You are still connected to a session" #: ../code/window.cpp:190 msgid "Do you want to close Gobby nevertheless?" msgstr "Do you want to close Gobby nevertheless?" #: ../code/window.cpp:196 msgid "C_ancel" msgstr "C_ancel" #: ../gobby-0.5.desktop.in.h:1 msgid "Gobby" msgstr "Gobby" #: ../gobby-0.5.desktop.in.h:2 msgid "Collaborative Editor" msgstr "Collaborative Editor" #: ../gobby-0.5.desktop.in.h:3 msgid "Gobby Collaborative Editor (0.5)" msgstr "Gobby Collaborative Editor (0.5)" #: ../gobby-0.5.desktop.in.h:4 msgid "Edit text files collaboratively" msgstr "Edit text files collaboratively" #~ msgid "" #~ "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.\n" #~ "\n" #~ "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.\n" #~ "\n" #~ "You should have received a copy of the GNU General Public License along " #~ "with this program; if not, write to the Free Software Foundation, Inc., " #~ "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA" #~ msgstr "" #~ "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.\n" #~ "\n" #~ "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.\n" #~ "\n" #~ "You should have received a copy of the GNU General Public License along " #~ "with this program; if not, write to the Free Software Foundation, Inc., " #~ "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA" #~ msgid "User Join in progress..." #~ msgstr "User Join in progress..." #, fuzzy #~ msgid "Failed to resolve \"%1\": %2" #~ msgstr "Could not resolve \"%1\"" #~ msgid "Resolving \"%1\"..." #~ msgstr "Resolving \"%1\"..." #~ msgid "Paths" #~ msgstr "Paths" #~ msgid "" #~ "The directory into which locally hosted sessions are permanently stored" #~ msgstr "" #~ "The directory into which locally hosted sessions are permanently stored" #, fuzzy #~ msgid "Subscription already in progress" #~ msgstr "Subscription failed" #, fuzzy #~ msgid "Whether to show the user list" #~ msgstr "Whether to show the user list for the chat" po/fr.po000066400000000000000000001154661304552132100124540ustar00rootroot00000000000000# French translation of Gobby. # Copyright (C) 2010, Listed translators # This file is distributed under the same license as the Gobby package. # Claude Paroz , 2010 # msgid "" msgstr "" "Project-Id-Version: Gobby 0.5 fr\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2014-08-23 13:43-0400\n" "PO-Revision-Date: 2010-11-16 23:04+0100\n" "Last-Translator: Claude Paroz \n" "Language-Team: French \n" "Language: French\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" #: ../code/commands/auth-commands.cpp:196 #, fuzzy msgid "Password required" msgstr "Mot de passe nécessaire" #: ../code/commands/auth-commands.cpp:214 msgid "Incorrect password" msgstr "" #: ../code/commands/browser-commands.cpp:288 msgid "Subscribing to %1..." msgstr "Inscription à %1..." #: ../code/commands/browser-commands.cpp:301 msgid "Subscribing to chat on %1..." msgstr "Inscription à la discussion sur %1..." #: ../code/commands/browser-commands.cpp:591 msgid "Subscription failed" msgstr "Échec de l'inscription" #: ../code/commands/browser-context-commands.cpp:86 msgid "Could not find a location where to store the certificate" msgstr "" #: ../code/commands/browser-context-commands.cpp:153 msgid "_Remove" msgstr "" #: ../code/commands/browser-context-commands.cpp:188 #, fuzzy msgid "_Disconnect from Server" msgstr "Connexion à un serveur" #: ../code/commands/browser-context-commands.cpp:220 #, fuzzy msgid "Create _Account..." msgstr "Créer un do_cument..." #: ../code/commands/browser-context-commands.cpp:245 msgid "Create Do_cument..." msgstr "Créer un do_cument..." #: ../code/commands/browser-context-commands.cpp:280 #, fuzzy msgid "Create Di_rectory..." msgstr "Créer un dossier..." #: ../code/commands/browser-context-commands.cpp:293 msgid "_Open Document..." msgstr "_Ouvrir un document..." #: ../code/commands/browser-context-commands.cpp:312 msgid "_Permissions..." msgstr "" #: ../code/commands/browser-context-commands.cpp:324 msgid "D_elete" msgstr "_Supprimer" #: ../code/commands/browser-context-commands.cpp:405 msgid "Choose a name for the directory" msgstr "Choisissez un nom pour le dossier" #: ../code/commands/browser-context-commands.cpp:406 msgid "Choose a name for the document" msgstr "Choisissez un nom pour le document" #: ../code/commands/browser-context-commands.cpp:407 msgid "_Directory Name:" msgstr "Nom du _dossier :" #: ../code/commands/browser-context-commands.cpp:408 msgid "_Document Name:" msgstr "Nom du _document :" #: ../code/commands/browser-context-commands.cpp:411 msgid "C_reate" msgstr "C_réer" #: ../code/commands/browser-context-commands.cpp:415 msgid "New Directory" msgstr "Nouveau dossier" #: ../code/commands/browser-context-commands.cpp:416 #: ../code/commands/file-tasks/task-new.cpp:36 msgid "New Document" msgstr "Nouveau document" #: ../code/commands/browser-context-commands.cpp:436 #: ../code/commands/file-tasks/task-open-file.cpp:22 msgid "Choose a text file to open" msgstr "Choisissez un fichier texte à ouvrir" #: ../code/commands/browser-context-commands.cpp:558 #: ../code/commands/browser-context-commands.cpp:569 msgid "Account successfully created" msgstr "" #: ../code/commands/browser-context-commands.cpp:562 msgid "When re-connecting to the server, the new account will be used." msgstr "" #: ../code/commands/browser-context-commands.cpp:573 msgid "" "The certificate has been stored at %1.\n" "\n" "To login to this account, set the certificate in Gobby's preferences and re-" "connect to the server." msgstr "" #: ../code/commands/browser-context-commands.cpp:586 #, fuzzy msgid "Failed to create account" msgstr "Impossible de créer le document « %1 »" #: ../code/commands/browser-context-commands.cpp:589 msgid "Could not save the certificate for the account: %1" msgstr "" #: ../code/commands/file-tasks/task-export-html.cpp:27 msgid "Choose a location to export document \"%1\" to" msgstr "Choisissez un emplacement pour y exporter le document « %1 »" #: ../code/commands/file-tasks/task-open.cpp:46 msgid "Querying \"%1\"..." msgstr "Requête vers « %1 »..." #: ../code/commands/file-tasks/task-open.cpp:103 #: ../code/operations/operation-open.cpp:474 #: ../code/operations/operation-open-multiple.cpp:180 msgid "Failed to open document \"%1\"" msgstr "Le document « %1 » n'a pas pu être ouvert" #: ../code/commands/file-tasks/task-save.cpp:24 msgid "Choose a location to save document \"%1\" to" msgstr "Choisissez un emplacement pour y enregistrer le document « %1 »" #. Help browser could not be invoked, show an error message to the user. #: ../code/commands/help-commands.cpp:70 msgid "There was an error displaying help." msgstr "Une erreur est survenue durant l'affichage de l'aide" #: ../code/commands/help-commands.cpp:93 msgid "Contributors:" msgstr "Contributeurs :" #: ../code/commands/help-commands.cpp:99 msgid "British English:" msgstr "Anglais britannique :" #: ../code/commands/help-commands.cpp:101 msgid "German:" msgstr "Allemand :" #: ../code/commands/help-commands.cpp:118 msgid "" "Permission to use, copy, modify, and/or distribute this software for any " "urpose with or without fee is hereby granted, provided that the above " "copyright notice and this permission notice appear in all copies.\n" "\n" "THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES " "WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF " "MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY " "SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES " "WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION " "OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN " "CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE." msgstr "" #: ../code/commands/subscription-commands.cpp:148 msgid "" "The connection to the publisher of this document has been lost. Further " "changes to the document could not be synchronized to others anymore, " "therefore the document cannot be edited anymore.\n" "\n" "Please note also that it is possible that not all of your latest changes " "have reached the publisher before the connection was lost." msgstr "" "La connexion à l'éditeur de ce document a été interrompue. De nouvelles " "modifications au document ne pourraient plus être partagées avec les autres, " "c'est pourquoi le document ne peut plus être modifié.\n" "\n" "Sachez également qu'il n'est pas certain que vos dernières modifications " "aient pu être transmises à l'éditeur avant la perte de la connexion." #: ../code/commands/synchronization-commands.cpp:30 msgid "Synchronization in progress... %1%%" msgstr "Synchronisation en cours... %1 %%" #. Document cannot be used if an error happened #. during synchronization. #: ../code/commands/synchronization-commands.cpp:42 msgid "This document cannot be used." msgstr "Ce document est inutilisable." #: ../code/commands/synchronization-commands.cpp:45 #: ../code/commands/user-join-commands.cpp:43 msgid "" "If you have an idea what could have caused the problem, then you may attempt " "to solve it and try again (after having closed this document). Otherwise it " "is most likely a bug in the software. In that case, please file a bug report " "at http://gobby.0x539.de/trac/newticket and provide as much information as " "you can, including what you did when the problem occurred and how to " "reproduce the problem (if possible) so that we can fix the problem in a " "later version. Thank you." msgstr "" "Si vous avez une idée sur la cause de ce problème, vous pouvez tenter de le " "résoudre puis essayer à nouveau (après avoir fermé ce document). Sinon, " "c'est probablement une anomalie du logiciel. Dans ce cas, veuillez signaler " "cette erreur dans un rapport sur http://gobby.0x539.de/trac/newticket et " "indiquer le maximum d'informations, y compris ce que vous faisiez lorsque le " "problème est survenu et comment il est possible de le reproduire (le cas " "échéant), afin que nous puissions corriger cette erreur dans une prochaine " "version. Merci." #: ../code/commands/synchronization-commands.cpp:59 msgid "Synchronization failed: %1" msgstr "Échec de synchronisation : %1" #. TODO: Adjust this for chat sessions #: ../code/commands/user-join-commands.cpp:38 msgid "" "You can still watch others editing the document, but you cannot edit it " "yourself." msgstr "" "Vous pouvez encore voir les modifications des autres personnes, mais vous ne " "pouvez plus modifier ce document vous-même." #: ../code/commands/user-join-commands.cpp:57 msgid "User Join failed: %1" msgstr "Échec de l'adhésion d'un utilisateur : %1" #: ../code/commands/user-join-commands.cpp:64 msgid "Permissions are not granted to modify the document." msgstr "" #: ../code/core/browser.cpp:111 msgid "_Direct Connection" msgstr "Connexion _directe" #: ../code/core/browser.cpp:113 msgid "Host Name:" msgstr "Nom d'hôte :" #: ../code/core/certificatemanager.cpp:83 #, c-format msgid "Failed to write Diffie-Hellman parameters to \"%s\": %s" msgstr "" #: ../code/core/certificatemanager.cpp:276 #, c-format msgid "Failed to read Diffie-Hellman parameters: %s" msgstr "" #: ../code/core/certificatemanager.cpp:338 msgid "File does not contain a X.509 certificate" msgstr "" #: ../code/core/certificatemanager.cpp:414 msgid "Certificate does not belong to the chosen key" msgstr "" #: ../code/core/foldermanager.cpp:322 msgid "The document has been removed from the server." msgstr "" #: ../code/core/header.cpp:203 msgid "_None" msgstr "_Aucun" #: ../code/core/header.cpp:227 msgid "_File" msgstr "_Fichier" #: ../code/core/header.cpp:230 msgid "_Open..." msgstr "_Ouvrir..." #: ../code/core/header.cpp:233 msgid "Open _Location..." msgstr "Ouvrir un _emplacement..." #: ../code/core/header.cpp:237 msgid "Save _As..." msgstr "Enregistrer _sous..." #: ../code/core/header.cpp:241 ../code/core/iconmanager.cpp:43 msgid "Save All" msgstr "Tout enregistrer" #: ../code/core/header.cpp:241 msgid "Save all open files locally" msgstr "Enregistrer localement tous les fichiers ouverts" #: ../code/core/header.cpp:243 msgid "Export As _HTML..." msgstr "Exporter en _HTML..." #: ../code/core/header.cpp:246 msgid "Connect _to Server..." msgstr "Se _connecter à un serveur..." #: ../code/core/header.cpp:251 msgid "_Edit" msgstr "É_dition" #: ../code/core/header.cpp:259 msgid "_Find..." msgstr "_Rechercher..." #: ../code/core/header.cpp:261 msgid "Find Ne_xt" msgstr "Rechercher le _suivant" #: ../code/core/header.cpp:262 msgid "Find next match of phrase searched for" msgstr "Rechercher la prochaine occurrence du texte recherché" #: ../code/core/header.cpp:265 msgid "Find Pre_vious" msgstr "Rechercher le _précédent" #: ../code/core/header.cpp:266 msgid "Find previous match of phrase searched for" msgstr "Rechercher la précédente occurrence du texte recherché" #: ../code/core/header.cpp:271 msgid "Find and Rep_lace..." msgstr "Rechercher et re_mplacer..." #: ../code/core/header.cpp:275 msgid "Go to _Line..." msgstr "Aller à la _ligne..." #: ../code/core/header.cpp:279 msgid "Pr_eferences..." msgstr "_Préférences..." #: ../code/core/header.cpp:281 msgid "_View" msgstr "_Affichage" #: ../code/core/header.cpp:284 msgid "Reset User Colors" msgstr "Réinitialiser les couleurs des utilisateurs" #: ../code/core/header.cpp:285 msgid "Hide user colors in current document" msgstr "Masquer les couleurs des utilisateurs dans le document actuel" #: ../code/core/header.cpp:289 msgid "Fullscreen" msgstr "" #: ../code/core/header.cpp:290 msgid "Show the editing window in fullscreen mode" msgstr "" #: ../code/core/header.cpp:294 msgid "Zoom In" msgstr "" #: ../code/core/header.cpp:297 msgid "Zoom Out" msgstr "" #: ../code/core/header.cpp:300 msgid "View Toolbar" msgstr "Afficher la barre d'outils" #: ../code/core/header.cpp:301 msgid "Whether to show the toolbar" msgstr "Indique s'il faut afficher la barre d'outils" #: ../code/core/header.cpp:305 msgid "View Statusbar" msgstr "Afficher la barre d'état" #: ../code/core/header.cpp:306 msgid "Whether to show the statusbar" msgstr "Indique s'il faut afficher la barre d'état" #: ../code/core/header.cpp:310 msgid "View Document Browser" msgstr "Afficher le navigateur de document" #: ../code/core/header.cpp:311 msgid "Whether to show the document browser" msgstr "Indique s'il faut afficher le navigateur de document" #: ../code/core/header.cpp:315 msgid "View Chat" msgstr "Afficher la discussion" #: ../code/core/header.cpp:316 msgid "Whether to show the chat pane" msgstr "Indique s'il faut afficher le volet de discussion" #: ../code/core/header.cpp:320 msgid "View Document User List" msgstr "Afficher la liste des utilisateurs du document" #: ../code/core/header.cpp:321 msgid "Whether to show the user list for documents" msgstr "Indique s'il faut afficher la liste des utilisateurs du document" #: ../code/core/header.cpp:325 msgid "View Chat User List" msgstr "Afficher la liste des utilisateurs de la discussion" #: ../code/core/header.cpp:326 msgid "Whether to show the user list for the chat" msgstr "Indique s'il faut afficher la liste des utilisateurs de la discussion" #: ../code/core/header.cpp:330 msgid "_Highlight Mode" msgstr "_Mode de coloration" #: ../code/core/header.cpp:336 msgid "_Help" msgstr "Aid_e" #: ../code/core/header.cpp:339 msgid "_Contents" msgstr "_Sommaire" #: ../code/core/header.cpp:340 msgid "Opens the Gobby manual" msgstr "Ouvre le manuel de Gobby" #: ../code/core/header.cpp:343 msgid "_About..." msgstr "À _propos..." #: ../code/core/header.cpp:344 msgid "Shows Gobby's copyright and credits" msgstr "Affiche le copyright et les remerciements de Gobby" #: ../code/core/iconmanager.cpp:49 msgid "User list" msgstr "Liste des utilisateurs" #: ../code/core/iconmanager.cpp:55 msgid "Document list" msgstr "Liste des documents" #: ../code/core/iconmanager.cpp:61 ../code/window.cpp:52 msgid "Chat" msgstr "Discussion" #: ../code/core/iconmanager.cpp:68 msgid "User Color Indicator" msgstr "Indicateur des couleurs des utilisateurs" #: ../code/core/selfhoster.cpp:103 msgid "Generating 2048-bit Diffie-Hellman parameters..." msgstr "" #: ../code/core/selfhoster.cpp:135 #, fuzzy msgid "Failed to generate Diffie-Hellman parameters" msgstr "Échec de suppression du nœud « %1 »" #: ../code/core/selfhoster.cpp:137 msgid "" "This means that Perfect Forward Secrecy (PFS) is not available. Restart " "Gobby to re-try generating the parameters. The specific error was:\n" "\n" "%1" msgstr "" #: ../code/core/selfhoster.cpp:262 msgid "" "In order to start sharing your documents, choose a private key and " "certificate or create a new pair in the preferences" msgstr "" #: ../code/core/selfhoster.cpp:282 #, fuzzy msgid "Failed to share documents" msgstr "L'enregistrement du document « %1 » a échoué" #: ../code/core/sessionuserview.cpp:39 msgid "User List" msgstr "Liste des utilisateurs" #: ../code/core/statusbar.cpp:418 msgid "Ln %1, Col %2\t%3" msgstr "Lig %1, col %2\t%3" #: ../code/core/statusbar.cpp:421 msgid "OVR" msgstr "ÉCR" #: ../code/core/statusbar.cpp:421 msgid "INS" msgstr "INS" #: ../code/core/textsessionview.cpp:638 msgid "Text written by %1" msgstr "Texte écrit par %1" #: ../code/core/textsessionview.cpp:644 msgid "Unowned text" msgstr "Texte anonyme" #: ../code/core/userlist.cpp:108 msgid "Users" msgstr "Utilisateurs" #: ../code/dialogs/connection-dialog.cpp:26 msgid "Connect to Server" msgstr "Connexion à un serveur" #: ../code/dialogs/connection-dialog.cpp:30 msgid "Please enter a host name with which to establish a connection." msgstr "Veuillez indiquer un nom d'hôte pour établir la connexion." #: ../code/dialogs/connection-dialog.cpp:32 msgid "_Remote Endpoint:" msgstr "Point d'_accès distant :" #: ../code/dialogs/document-location-dialog.cpp:28 msgid "Document Name:" msgstr "Nom du document :" #: ../code/dialogs/document-location-dialog.cpp:155 msgid "Select document's target location" msgstr "Sélectionnez l'emplacement cible du document" #: ../code/dialogs/document-location-dialog.cpp:157 msgid "Choose a directory to create the document into:" msgstr "Choisissez un dossier dans lequel créer le document :" #: ../code/dialogs/document-location-dialog.cpp:163 msgid "Select documents' target location" msgstr "Sélectionnez l'emplacement cible du document" #: ../code/dialogs/document-location-dialog.cpp:165 msgid "Choose a directory to create the documents into:" msgstr "Choisissez un dossier dans lequel créer les documents :" #: ../code/dialogs/find-dialog.cpp:44 ../code/dialogs/find-dialog.cpp:142 msgid "Find" msgstr "Rechercher" #: ../code/dialogs/find-dialog.cpp:47 msgid "_Search for:" msgstr "_Rechercher :" #: ../code/dialogs/find-dialog.cpp:49 msgid "Replace _with:" msgstr "Rem_placer par :" #: ../code/dialogs/find-dialog.cpp:51 msgid "_Match case" msgstr "Re_specter la casse" #: ../code/dialogs/find-dialog.cpp:52 msgid "Match _entire word only" msgstr "_Mots entiers seulement" #: ../code/dialogs/find-dialog.cpp:53 msgid "Search _backwards" msgstr "Rechercher en _arrière" #: ../code/dialogs/find-dialog.cpp:54 msgid "Wra_p around" msgstr "Recherche _circulaire" #: ../code/dialogs/find-dialog.cpp:100 msgid "Replace _All" msgstr "_Tout remplacer" #: ../code/dialogs/find-dialog.cpp:102 msgid "_Replace" msgstr "R_emplacer" #: ../code/dialogs/find-dialog.cpp:142 msgid "Replace" msgstr "Remplacer" #: ../code/dialogs/find-dialog.cpp:161 ../code/dialogs/find-dialog.cpp:177 msgid "Phrase \"%1\" has not been found" msgstr "Le texte « %1 » n'a pas été trouvé" #: ../code/dialogs/find-dialog.cpp:335 msgid "No occurrence has been replaced" msgstr "Aucune occurrence n'a été remplacée" #: ../code/dialogs/find-dialog.cpp:341 msgid "%1 occurrence has been replaced" msgid_plural "%1 occurrences have been replaced" msgstr[0] "%1 occurrence a été remplacée" msgstr[1] "%1 occurrences ont été remplacées" #: ../code/dialogs/goto-dialog.cpp:26 msgid "Go to line" msgstr "Aller à la ligne" #: ../code/dialogs/goto-dialog.cpp:29 msgid "Line _number:" msgstr "_Numéro de ligne :" #: ../code/dialogs/goto-dialog.cpp:50 msgid "Go To _Line" msgstr "Aller à la _ligne" #: ../code/dialogs/initial-dialog.cpp:76 msgid "Generating 2048-bit RSA private key..." msgstr "" #: ../code/dialogs/initial-dialog.cpp:99 #, fuzzy msgid "Failed to generate private key" msgstr "Échec de suppression du nœud « %1 »" #: ../code/dialogs/initial-dialog.cpp:102 msgid "" "%1\n" "\n" "You can try again to create a key in the Security tab of the preferences " "dialog." msgstr "" #: ../code/dialogs/initial-dialog.cpp:141 msgid "Failed to generate self-signed certificate" msgstr "" #: ../code/dialogs/initial-dialog.cpp:144 msgid "" "%1\n" "\n" "You can try again to create a certificate in the Security tab of the " "preferences dialog." msgstr "" #: ../code/dialogs/initial-dialog.cpp:198 msgid "Choose a user color" msgstr "Choisissez une couleur d'utilisateur" #: ../code/dialogs/initial-dialog.cpp:204 msgid "Welcome to Gobby" msgstr "Bienvenue dans Gobby" #: ../code/dialogs/initial-dialog.cpp:216 msgid "" "Before we start, a few options need to be configured. You can later change " "them by choosing Edit ▸ Preferences from the menu." msgstr "" "Avant de commencer, quelques options doivent être configurées. Il est " "possible de les modifier ultérieurement en choisissant Édition ▸ Préférences " "dans le menu." #: ../code/dialogs/initial-dialog.cpp:224 msgid "User Name" msgstr "Nom d'utilisateur" #: ../code/dialogs/initial-dialog.cpp:226 msgid "Your name as shown to other users." msgstr "Votre nom tel qu'il apparaîtra pour les autres personnes." #: ../code/dialogs/initial-dialog.cpp:235 msgid "User Color" msgstr "Couleur d'utilisateur" #: ../code/dialogs/initial-dialog.cpp:237 msgid "The color with which text you have written is branded." msgstr "La couleur qui marquera le texte que vous écrivez." #: ../code/dialogs/initial-dialog.cpp:256 #, fuzzy msgid "Remote Connections" msgstr "Connexion sécurisée" #: ../code/dialogs/initial-dialog.cpp:262 #: ../code/dialogs/preferences-dialog.cpp:217 msgid "Allow remote users to edit local documents" msgstr "" #: ../code/dialogs/initial-dialog.cpp:268 msgid "Ask remote users for a password" msgstr "" #: ../code/dialogs/initial-dialog.cpp:273 #: ../code/dialogs/preferences-dialog.cpp:220 #, fuzzy msgid "Password:" msgstr "_Mot de passe du serveur :" #: ../code/dialogs/initial-dialog.cpp:297 msgid "No authentication (Not recommended)" msgstr "" #: ../code/dialogs/initial-dialog.cpp:304 msgid "" "Don't authenticate ourselves to remote users. Data transfer will be " "unencrypted." msgstr "" #: ../code/dialogs/initial-dialog.cpp:313 msgid "Create a self-signed certificate (Recommended)" msgstr "" #: ../code/dialogs/initial-dialog.cpp:321 msgid "" "It may take a minute or two until remote users can connect while the " "security certificate is being generated." msgstr "" #: ../code/dialogs/initial-dialog.cpp:331 msgid "Use an existing certificate (Expert option)" msgstr "" #: ../code/dialogs/initial-dialog.cpp:338 msgid "" "Use an existing private key and certificate for authentication. The files " "must be in PEM format." msgstr "" #: ../code/dialogs/initial-dialog.cpp:347 #: ../code/dialogs/preferences-dialog.cpp:766 msgid "Private key:" msgstr "" #: ../code/dialogs/initial-dialog.cpp:349 #: ../code/dialogs/preferences-dialog.cpp:772 msgid "Certificate:" msgstr "" #: ../code/dialogs/open-location-dialog.cpp:26 msgid "Open Location" msgstr "Ouvrir un emplacement" #: ../code/dialogs/open-location-dialog.cpp:27 msgid "Enter the _location (URI) of the file you would like to open:" msgstr "Saisissez l'_emplacement (URI) du fichier que vous souhaitez ouvrir :" #: ../code/dialogs/password-dialog.cpp:25 msgid "Password Required" msgstr "Mot de passe nécessaire" #: ../code/dialogs/password-dialog.cpp:31 msgid "Connection to host \"%1\" requires a password." msgstr "La connexion au serveur « %1 » exige un mot de passe." #: ../code/dialogs/password-dialog.cpp:32 msgid "Invalid password for host \"%1\". Please try again." msgstr "" "Le mot de passe pour le serveur « %1 » n'est pas valable. Essayez encore une " "fois." #: ../code/dialogs/password-dialog.cpp:35 msgid "Server _Password:" msgstr "_Mot de passe du serveur :" #: ../code/dialogs/preferences-dialog.cpp:198 msgid "Settings" msgstr "Paramètres" #: ../code/dialogs/preferences-dialog.cpp:199 #, fuzzy msgid "Remote Users" msgstr "Réinitialiser les couleurs des utilisateurs" #: ../code/dialogs/preferences-dialog.cpp:200 #, fuzzy msgid "Local Documents" msgstr "Nouveau document" #: ../code/dialogs/preferences-dialog.cpp:202 msgid "User name:" msgstr "Nom d'utilisateur :" #: ../code/dialogs/preferences-dialog.cpp:204 msgid "User color:" msgstr "Couleur d'utilisateur :" #: ../code/dialogs/preferences-dialog.cpp:206 msgid "Color intensity:" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:208 msgid "Choose a new user color" msgstr "Choisissez une nouvelle couleur d'utilisateur" #: ../code/dialogs/preferences-dialog.cpp:209 msgid "Show cursors of remote users" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:210 msgid "Show selections of remote users" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:212 #, fuzzy msgid "Highlight current line of remote users" msgstr "Surligner la ligne actuelle" #: ../code/dialogs/preferences-dialog.cpp:214 msgid "Indicate cursor position of remote users in the scrollbar" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:219 msgid "Require remote users to enter a password" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:222 msgid "Port:" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:226 msgid "Remember local documents after Gobby restart" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:227 #, fuzzy msgid "Local documents directory:" msgstr "Dossier de l'hôte :" #: ../code/dialogs/preferences-dialog.cpp:423 msgid "Tab Stops" msgstr "Tabulations" #: ../code/dialogs/preferences-dialog.cpp:424 msgid "Indentation" msgstr "Indentation" #: ../code/dialogs/preferences-dialog.cpp:425 msgid "Home/End Behavior" msgstr "Comportement de Origine/Fin" #: ../code/dialogs/preferences-dialog.cpp:426 msgid "File Saving" msgstr "Enregistrement de fichiers" #: ../code/dialogs/preferences-dialog.cpp:427 msgid "_Tab width:" msgstr "_Largeur des tabulations :" #: ../code/dialogs/preferences-dialog.cpp:429 msgid "Insert _spaces instead of tabs" msgstr "Insérer des _espaces au lieu de tabulations" #: ../code/dialogs/preferences-dialog.cpp:430 msgid "Enable automatic _indentation" msgstr "Activer l'_indentation automatique" #: ../code/dialogs/preferences-dialog.cpp:431 msgid "Smart _home/end" msgstr "_Origine/fin intelligents" #: ../code/dialogs/preferences-dialog.cpp:432 msgid "Enable _automatic saving of documents" msgstr "_Activer l'enregistrement automatique des documents" #: ../code/dialogs/preferences-dialog.cpp:434 msgid "Autosave interval in _minutes:" msgstr "Intervalle d'enregistrement en _minutes :" #: ../code/dialogs/preferences-dialog.cpp:455 msgid "" "With this option enabled, Home/End keys move to first/last character before " "going to the start/end of the line." msgstr "" "Quand cette option est activée, les touches Origine/Fin se placent au " "premier/dernier caractère avant d'aller au début/à la fin de la ligne." #: ../code/dialogs/preferences-dialog.cpp:525 msgid "Text Wrapping" msgstr "Longues lignes" #: ../code/dialogs/preferences-dialog.cpp:526 msgid "Line Numbers" msgstr "Numéros de ligne" #: ../code/dialogs/preferences-dialog.cpp:527 msgid "Current Line" msgstr "Ligne actuelle" #: ../code/dialogs/preferences-dialog.cpp:528 msgid "Right Margin" msgstr "Marge de droite" #: ../code/dialogs/preferences-dialog.cpp:529 msgid "Bracket Matching" msgstr "Correspondance des parenthèses" #: ../code/dialogs/preferences-dialog.cpp:530 msgid "Whitespace Display" msgstr "Affichage des blancs" #: ../code/dialogs/preferences-dialog.cpp:531 msgid "Enable text wrapping" msgstr "Activer le retour à la ligne" #: ../code/dialogs/preferences-dialog.cpp:532 msgid "Do not split words over two lines" msgstr "Ne pas couper les mots sur deux lignes" #: ../code/dialogs/preferences-dialog.cpp:533 msgid "Display line numbers" msgstr "Afficher les numéros de ligne" #: ../code/dialogs/preferences-dialog.cpp:534 msgid "Highlight current line" msgstr "Surligner la ligne actuelle" #: ../code/dialogs/preferences-dialog.cpp:535 msgid "Display right margin" msgstr "Afficher la marge de droite" #: ../code/dialogs/preferences-dialog.cpp:536 msgid "Right margin at column:" msgstr "Marge de droite à la colonne :" #: ../code/dialogs/preferences-dialog.cpp:537 msgid "Highlight matching bracket" msgstr "Surligner les parenthèses correspondantes" #: ../code/dialogs/preferences-dialog.cpp:589 msgid "Display no whitespace" msgstr "N'afficher aucun type d'espace" #: ../code/dialogs/preferences-dialog.cpp:592 msgid "Display spaces" msgstr "Afficher les espaces" #: ../code/dialogs/preferences-dialog.cpp:596 msgid "Display tabs" msgstr "Afficher les tabulations" #: ../code/dialogs/preferences-dialog.cpp:600 msgid "Display tabs and spaces" msgstr "Afficher les tabulations et les espaces" #: ../code/dialogs/preferences-dialog.cpp:651 msgid "Toolbar" msgstr "Barre d'outils" #: ../code/dialogs/preferences-dialog.cpp:652 msgid "Font" msgstr "Police" #: ../code/dialogs/preferences-dialog.cpp:653 msgid "Color Scheme" msgstr "Jeu de couleurs" #: ../code/dialogs/preferences-dialog.cpp:661 msgid "Show text only" msgstr "Afficher uniquement le texte" #: ../code/dialogs/preferences-dialog.cpp:663 msgid "Show icons only" msgstr "Afficher unqiuement les icônes" #: ../code/dialogs/preferences-dialog.cpp:665 msgid "Show both icons and text" msgstr "Afficher les icônes et le texte" #: ../code/dialogs/preferences-dialog.cpp:667 msgid "Show text besides icons" msgstr "Afficher le texte à côté des icônes" #: ../code/dialogs/preferences-dialog.cpp:756 msgid "Trusted CAs" msgstr "Autorités de confiance" #: ../code/dialogs/preferences-dialog.cpp:757 msgid "Secure Connection" msgstr "Connexion sécurisée" #: ../code/dialogs/preferences-dialog.cpp:758 #, fuzzy msgid "Authentication" msgstr "Indentation" #: ../code/dialogs/preferences-dialog.cpp:759 msgid "Select a file containing trusted CAs" msgstr "" "Choisissez un fichier contenant les autorités de certification de confiance" #: ../code/dialogs/preferences-dialog.cpp:764 #, fuzzy msgid "None" msgstr "_Aucun" #: ../code/dialogs/preferences-dialog.cpp:765 msgid "Use a certificate" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:767 msgid "Select a private key file" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:769 #: ../code/dialogs/preferences-dialog.cpp:776 #, fuzzy msgid "Create New..." msgstr "Créer un do_cument..." #: ../code/dialogs/preferences-dialog.cpp:773 msgid "Select a certificate file" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:801 msgid "Use TLS if possible" msgstr "Utiliser TSL si possible" #: ../code/dialogs/preferences-dialog.cpp:804 msgid "Always use TLS" msgstr "Toujours utiliser TLS" #: ../code/dialogs/preferences-dialog.cpp:892 msgid "Error reading file:" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:916 msgid "2048-bit RSA private key is being generated, please wait..." msgstr "" #: ../code/dialogs/preferences-dialog.cpp:948 msgid "Select a location for the generated key" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:966 msgid "Select a location for the generated certificate" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:1068 msgid "Preferences" msgstr "Préférences" #: ../code/dialogs/preferences-dialog.cpp:1073 msgid "User" msgstr "Utilisateur" #: ../code/dialogs/preferences-dialog.cpp:1074 msgid "Editor" msgstr "Éditeur" #: ../code/dialogs/preferences-dialog.cpp:1075 msgid "View" msgstr "Affichage" #: ../code/dialogs/preferences-dialog.cpp:1076 msgid "Appearance" msgstr "Apparence" #: ../code/dialogs/preferences-dialog.cpp:1077 msgid "Security" msgstr "Sécurité" #: ../code/main.cpp:194 msgid "Gobby options" msgstr "Options de Gobby" #: ../code/main.cpp:194 msgid "Options related to Gobby" msgstr "Options liées à Gobby" #: ../code/main.cpp:199 msgid "Display version information and exit" msgstr "Affiche l'information de version et quitte" #: ../code/main.cpp:206 msgid "Also start a new Gobby instance when there is one running already" msgstr "Démarre une nouvelle instance Gobby même s'il y en a déjà une" #: ../code/main.cpp:214 msgid "Connect to given host on startup, can be given multiple times" msgstr "" "Se connecte à l'hôte indiqué au démarrage, peut apparaître plusieurs fois" #: ../code/main.cpp:215 msgid "HOSTNAME" msgstr "NOM_HÒTE" #: ../code/operations/operation-delete.cpp:65 msgid "Removing node \"%1\"..." msgstr "Suppression du nœud « %1 »..." #: ../code/operations/operation-delete.cpp:75 msgid "Failed to delete node \"%1\"" msgstr "Échec de suppression du nœud « %1 »" #: ../code/operations/operation-export-html.cpp:168 #, c-format msgid "written by: %s" msgstr "écrit par : %s" #: ../code/operations/operation-export-html.cpp:241 msgid "" msgstr "" #. %1$s is session name/hostname #. %2$s is path within the session #. %3$s is current date as formatted by %c, #. %4$s is a link to the gobby site, it must be present because #. we need to handle that manually to insert a hyperlink #. instead of just printf'ing it. #: ../code/operations/operation-export-html.cpp:254 #, c-format msgid "Document generated from %1$s:%2$s at %3$s by %4$s" msgstr "Document généré à partir de %1$s:%2$s le %3$s par %4$s" #: ../code/operations/operation-export-html.cpp:395 msgid "Participants" msgstr "Participants" #: ../code/operations/operation-export-html.cpp:475 msgid "Exporting document \"%1\" to \"%2\" in HTML..." msgstr "Exportation HTML du document « %1 » vers « %2 »..." #: ../code/operations/operation-export-html.cpp:536 msgid "Failed to export document \"%1\" to HTML" msgstr "Impossible d'exporter le document « %1 » en HTML" #: ../code/operations/operation-new.cpp:75 msgid "Creating directory \"%1\"..." msgstr "Création du dossier « %1 »..." #: ../code/operations/operation-new.cpp:76 msgid "Creating document \"%1\"..." msgstr "Création du document « %1 »..." #: ../code/operations/operation-new.cpp:88 msgid "Failed to create directory \"%1\"" msgstr "Impossible de créer le dossier « %1 »" #: ../code/operations/operation-new.cpp:89 msgid "Failed to create document \"%1\"" msgstr "Impossible de créer le document « %1 »" #. Translators: This is the 8 bit encoding that is tried when #. autodetecting a file's encoding. #: ../code/operations/operation-open.cpp:38 msgid "ISO-8859-1" msgstr "ISO-8859-1" #: ../code/operations/operation-open.cpp:114 msgid "Opening document \"%1\"..." msgstr "Ouverture du document « %1 »..." #: ../code/operations/operation-open.cpp:134 msgid "" "The directory into which the new document was supposed to be inserted has " "been removed" msgstr "" "Le dossier dans lequel le document aurait dû être inséré a été supprimé" #: ../code/operations/operation-open.cpp:323 msgid "The file contains data not in the specified encoding" msgstr "" "Le fichier contient des données incompatibles avec le jeu de caractères " "indiqué" #: ../code/operations/operation-open.cpp:334 msgid "" "The file either contains data in an unknown encoding, or it contains binary " "data." msgstr "" "Soit le fichier contient des données dans un codage inconnu, soit il " "contient des données binaires." #: ../code/operations/operation-open-multiple.cpp:91 msgid "Parent folder was removed" msgstr "Le dossier parent a été supprimé" #: ../code/operations/operation-open-multiple.cpp:193 msgid "Failed to open multiple documents" msgstr "Impossible d'ouvrir plusieurs documents" #: ../code/operations/operation-save.cpp:97 msgid "Saving document \"%1\" to \"%2\"..." msgstr "Enregistrement du document « %1 » vers « %2 »..." #: ../code/operations/operation-save.cpp:226 msgid "" "The document contains one or more characters that cannot be encoded in the " "specified character coding." msgstr "" "Le document contient un ou plusieurs caractères qui ne peuvent pas être " "codés dans le jeu de caractères indiqué." #: ../code/operations/operation-save.cpp:287 msgid "Failed to save document \"%1\"" msgstr "L'enregistrement du document « %1 » a échoué" #: ../code/operations/operation-subscribe-path.cpp:33 msgid "Invalid path: \"%1\"" msgstr "" #: ../code/operations/operation-subscribe-path.cpp:45 #, fuzzy msgid "Invalid path component: \"%1\"" msgstr "Le document « %1 » n'a pas pu être ouvert" #: ../code/operations/operation-subscribe-path.cpp:135 #: ../code/operations/operation-subscribe-path.cpp:200 #, fuzzy msgid "Subscribing to \"%1\"..." msgstr "Inscription à %1..." #: ../code/operations/operation-subscribe-path.cpp:149 #: ../code/operations/operation-subscribe-path.cpp:212 #, fuzzy msgid "Failed to connect to \"%1\"" msgstr "Impossible de créer le dossier « %1 »" #: ../code/operations/operation-subscribe-path.cpp:164 msgid "URI scheme \"%1\" not supported" msgstr "" #: ../code/operations/operation-subscribe-path.cpp:193 #, fuzzy msgid "Connecting to \"%1\"..." msgstr "La connexion à « %1 » a échoué" #: ../code/operations/operation-subscribe-path.cpp:220 #: ../code/operations/operation-subscribe-path.cpp:319 #: ../code/operations/operation-subscribe-path.cpp:356 #: ../code/operations/operation-subscribe-path.cpp:452 #: ../code/operations/operation-subscribe-path.cpp:472 #, fuzzy msgid "Could not subscribe to \"%1\"" msgstr "Impossible de résoudre « %1 »" #: ../code/operations/operation-subscribe-path.cpp:322 #: ../code/operations/operation-subscribe-path.cpp:358 #, fuzzy msgid "Path \"%1\" does not exist" msgstr "Le périphérique « %1 » n'existe pas" #: ../code/util/file.cpp:65 ../code/util/file.cpp:74 msgid "Could not create directory \"%1\": %2" msgstr "Impossible de créer le " #: ../code/util/uri.cpp:91 msgid "Device \"%1\" does not exist" msgstr "Le périphérique « %1 » n'existe pas" #: ../code/window.cpp:104 msgid "This Computer" msgstr "" #: ../code/window.cpp:129 msgid "Document Browser" msgstr "Navigateur de documents" #: ../code/window.cpp:182 msgid "You are still connected to a session" msgstr "Vous êtes toujours connecté à une session" #: ../code/window.cpp:190 msgid "Do you want to close Gobby nevertheless?" msgstr "Souhaitez-vous tout de même fermer Gobby ?" #: ../code/window.cpp:196 msgid "C_ancel" msgstr "_Annuler" #: ../gobby-0.5.desktop.in.h:1 msgid "Gobby" msgstr "Gobby" #: ../gobby-0.5.desktop.in.h:2 msgid "Collaborative Editor" msgstr "Éditeur collaboratif" #: ../gobby-0.5.desktop.in.h:3 msgid "Gobby Collaborative Editor (0.5)" msgstr "Éditeur collaboratif Gobby (0.5)" #: ../gobby-0.5.desktop.in.h:4 msgid "Edit text files collaboratively" msgstr "Éditer des fichiers texte de manière collaborative" #~ msgid "" #~ "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.\n" #~ "\n" #~ "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.\n" #~ "\n" #~ "You should have received a copy of the GNU General Public License along " #~ "with this program; if not, write to the Free Software Foundation, Inc., " #~ "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA" #~ msgstr "" #~ "Ce programme est un logiciel libre ; vous pouvez le redistribuer et/ou le " #~ "modifier au titre des clauses de la Licence Publique Générale GNU, telle " #~ "que publiée par la Free Software Foundation ; soit la version 2 de la " #~ "Licence, ou (à votre discrétion) une version ultérieure quelconque.\n" #~ "Ce programme est distribué dans l'espoir qu'il sera utile, mais SANS " #~ "AUCUNE GARANTIE ; sans même une garantie implicite de COMMERCIABILITÉ ou " #~ "DE CONFORMITÉ À UNE UTILISATION PARTICULIÈRE. Voir la Licence Publique " #~ "Générale GNU pour plus de détails.\n" #~ "Vous devriez avoir reçu un exemplaire de la Licence Publique Générale GNU " #~ "avec ce programme ; si ce n'est pas le cas, écrivez à la Free Software " #~ "Foundation Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, " #~ "USA." #~ msgid "User Join in progress..." #~ msgstr "Adhésion d'utilisateur en cours..." #, fuzzy #~ msgid "Failed to resolve \"%1\": %2" #~ msgstr "Impossible de résoudre « %1 »" #~ msgid "Resolving \"%1\"..." #~ msgstr "Résolution de « %1 »..." #~ msgid "Paths" #~ msgstr "Chemins" #~ msgid "" #~ "The directory into which locally hosted sessions are permanently stored" #~ msgstr "" #~ "Le dossier dans lequel les sessions hébergées localement sont " #~ "enregistrées de façon permanente" po/gobby05.pot000066400000000000000000000706541304552132100134770ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2014-08-23 13:43-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" #: ../code/commands/auth-commands.cpp:196 msgid "Password required" msgstr "" #: ../code/commands/auth-commands.cpp:214 msgid "Incorrect password" msgstr "" #: ../code/commands/browser-commands.cpp:288 msgid "Subscribing to %1..." msgstr "" #: ../code/commands/browser-commands.cpp:301 msgid "Subscribing to chat on %1..." msgstr "" #: ../code/commands/browser-commands.cpp:591 msgid "Subscription failed" msgstr "" #: ../code/commands/browser-context-commands.cpp:86 msgid "Could not find a location where to store the certificate" msgstr "" #: ../code/commands/browser-context-commands.cpp:153 msgid "_Remove" msgstr "" #: ../code/commands/browser-context-commands.cpp:188 msgid "_Disconnect from Server" msgstr "" #: ../code/commands/browser-context-commands.cpp:220 msgid "Create _Account..." msgstr "" #: ../code/commands/browser-context-commands.cpp:245 msgid "Create Do_cument..." msgstr "" #: ../code/commands/browser-context-commands.cpp:280 msgid "Create Di_rectory..." msgstr "" #: ../code/commands/browser-context-commands.cpp:293 msgid "_Open Document..." msgstr "" #: ../code/commands/browser-context-commands.cpp:312 msgid "_Permissions..." msgstr "" #: ../code/commands/browser-context-commands.cpp:324 msgid "D_elete" msgstr "" #: ../code/commands/browser-context-commands.cpp:405 msgid "Choose a name for the directory" msgstr "" #: ../code/commands/browser-context-commands.cpp:406 msgid "Choose a name for the document" msgstr "" #: ../code/commands/browser-context-commands.cpp:407 msgid "_Directory Name:" msgstr "" #: ../code/commands/browser-context-commands.cpp:408 msgid "_Document Name:" msgstr "" #: ../code/commands/browser-context-commands.cpp:411 msgid "C_reate" msgstr "" #: ../code/commands/browser-context-commands.cpp:415 msgid "New Directory" msgstr "" #: ../code/commands/browser-context-commands.cpp:416 #: ../code/commands/file-tasks/task-new.cpp:36 msgid "New Document" msgstr "" #: ../code/commands/browser-context-commands.cpp:436 #: ../code/commands/file-tasks/task-open-file.cpp:22 msgid "Choose a text file to open" msgstr "" #: ../code/commands/browser-context-commands.cpp:558 #: ../code/commands/browser-context-commands.cpp:569 msgid "Account successfully created" msgstr "" #: ../code/commands/browser-context-commands.cpp:562 msgid "When re-connecting to the server, the new account will be used." msgstr "" #: ../code/commands/browser-context-commands.cpp:573 msgid "" "The certificate has been stored at %1.\n" "\n" "To login to this account, set the certificate in Gobby's preferences and re-" "connect to the server." msgstr "" #: ../code/commands/browser-context-commands.cpp:586 msgid "Failed to create account" msgstr "" #: ../code/commands/browser-context-commands.cpp:589 msgid "Could not save the certificate for the account: %1" msgstr "" #: ../code/commands/file-tasks/task-export-html.cpp:27 msgid "Choose a location to export document \"%1\" to" msgstr "" #: ../code/commands/file-tasks/task-open.cpp:46 msgid "Querying \"%1\"..." msgstr "" #: ../code/commands/file-tasks/task-open.cpp:103 #: ../code/operations/operation-open.cpp:474 #: ../code/operations/operation-open-multiple.cpp:180 msgid "Failed to open document \"%1\"" msgstr "" #: ../code/commands/file-tasks/task-save.cpp:24 msgid "Choose a location to save document \"%1\" to" msgstr "" #. Help browser could not be invoked, show an error message to the user. #: ../code/commands/help-commands.cpp:70 msgid "There was an error displaying help." msgstr "" #: ../code/commands/help-commands.cpp:93 msgid "Contributors:" msgstr "" #: ../code/commands/help-commands.cpp:99 msgid "British English:" msgstr "" #: ../code/commands/help-commands.cpp:101 msgid "German:" msgstr "" #: ../code/commands/help-commands.cpp:118 msgid "" "Permission to use, copy, modify, and/or distribute this software for any " "urpose with or without fee is hereby granted, provided that the above " "copyright notice and this permission notice appear in all copies.\n" "\n" "THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES " "WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF " "MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY " "SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES " "WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION " "OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN " "CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE." msgstr "" #: ../code/commands/subscription-commands.cpp:148 msgid "" "The connection to the publisher of this document has been lost. Further " "changes to the document could not be synchronized to others anymore, " "therefore the document cannot be edited anymore.\n" "\n" "Please note also that it is possible that not all of your latest changes " "have reached the publisher before the connection was lost." msgstr "" #: ../code/commands/synchronization-commands.cpp:30 msgid "Synchronization in progress... %1%%" msgstr "" #. Document cannot be used if an error happened #. during synchronization. #: ../code/commands/synchronization-commands.cpp:42 msgid "This document cannot be used." msgstr "" #: ../code/commands/synchronization-commands.cpp:45 #: ../code/commands/user-join-commands.cpp:43 msgid "" "If you have an idea what could have caused the problem, then you may attempt " "to solve it and try again (after having closed this document). Otherwise it " "is most likely a bug in the software. In that case, please file a bug report " "at http://gobby.0x539.de/trac/newticket and provide as much information as " "you can, including what you did when the problem occurred and how to " "reproduce the problem (if possible) so that we can fix the problem in a " "later version. Thank you." msgstr "" #: ../code/commands/synchronization-commands.cpp:59 msgid "Synchronization failed: %1" msgstr "" #. TODO: Adjust this for chat sessions #: ../code/commands/user-join-commands.cpp:38 msgid "" "You can still watch others editing the document, but you cannot edit it " "yourself." msgstr "" #: ../code/commands/user-join-commands.cpp:57 msgid "User Join failed: %1" msgstr "" #: ../code/commands/user-join-commands.cpp:64 msgid "Permissions are not granted to modify the document." msgstr "" #: ../code/core/browser.cpp:111 msgid "_Direct Connection" msgstr "" #: ../code/core/browser.cpp:113 msgid "Host Name:" msgstr "" #: ../code/core/certificatemanager.cpp:83 #, c-format msgid "Failed to write Diffie-Hellman parameters to \"%s\": %s" msgstr "" #: ../code/core/certificatemanager.cpp:276 #, c-format msgid "Failed to read Diffie-Hellman parameters: %s" msgstr "" #: ../code/core/certificatemanager.cpp:338 msgid "File does not contain a X.509 certificate" msgstr "" #: ../code/core/certificatemanager.cpp:414 msgid "Certificate does not belong to the chosen key" msgstr "" #: ../code/core/foldermanager.cpp:322 msgid "The document has been removed from the server." msgstr "" #: ../code/core/header.cpp:203 msgid "_None" msgstr "" #: ../code/core/header.cpp:227 msgid "_File" msgstr "" #: ../code/core/header.cpp:230 msgid "_Open..." msgstr "" #: ../code/core/header.cpp:233 msgid "Open _Location..." msgstr "" #: ../code/core/header.cpp:237 msgid "Save _As..." msgstr "" #: ../code/core/header.cpp:241 ../code/core/iconmanager.cpp:43 msgid "Save All" msgstr "" #: ../code/core/header.cpp:241 msgid "Save all open files locally" msgstr "" #: ../code/core/header.cpp:243 msgid "Export As _HTML..." msgstr "" #: ../code/core/header.cpp:246 msgid "Connect _to Server..." msgstr "" #: ../code/core/header.cpp:251 msgid "_Edit" msgstr "" #: ../code/core/header.cpp:259 msgid "_Find..." msgstr "" #: ../code/core/header.cpp:261 msgid "Find Ne_xt" msgstr "" #: ../code/core/header.cpp:262 msgid "Find next match of phrase searched for" msgstr "" #: ../code/core/header.cpp:265 msgid "Find Pre_vious" msgstr "" #: ../code/core/header.cpp:266 msgid "Find previous match of phrase searched for" msgstr "" #: ../code/core/header.cpp:271 msgid "Find and Rep_lace..." msgstr "" #: ../code/core/header.cpp:275 msgid "Go to _Line..." msgstr "" #: ../code/core/header.cpp:279 msgid "Pr_eferences..." msgstr "" #: ../code/core/header.cpp:281 msgid "_View" msgstr "" #: ../code/core/header.cpp:284 msgid "Reset User Colors" msgstr "" #: ../code/core/header.cpp:285 msgid "Hide user colors in current document" msgstr "" #: ../code/core/header.cpp:289 msgid "Fullscreen" msgstr "" #: ../code/core/header.cpp:290 msgid "Show the editing window in fullscreen mode" msgstr "" #: ../code/core/header.cpp:294 msgid "Zoom In" msgstr "" #: ../code/core/header.cpp:297 msgid "Zoom Out" msgstr "" #: ../code/core/header.cpp:300 msgid "View Toolbar" msgstr "" #: ../code/core/header.cpp:301 msgid "Whether to show the toolbar" msgstr "" #: ../code/core/header.cpp:305 msgid "View Statusbar" msgstr "" #: ../code/core/header.cpp:306 msgid "Whether to show the statusbar" msgstr "" #: ../code/core/header.cpp:310 msgid "View Document Browser" msgstr "" #: ../code/core/header.cpp:311 msgid "Whether to show the document browser" msgstr "" #: ../code/core/header.cpp:315 msgid "View Chat" msgstr "" #: ../code/core/header.cpp:316 msgid "Whether to show the chat pane" msgstr "" #: ../code/core/header.cpp:320 msgid "View Document User List" msgstr "" #: ../code/core/header.cpp:321 msgid "Whether to show the user list for documents" msgstr "" #: ../code/core/header.cpp:325 msgid "View Chat User List" msgstr "" #: ../code/core/header.cpp:326 msgid "Whether to show the user list for the chat" msgstr "" #: ../code/core/header.cpp:330 msgid "_Highlight Mode" msgstr "" #: ../code/core/header.cpp:336 msgid "_Help" msgstr "" #: ../code/core/header.cpp:339 msgid "_Contents" msgstr "" #: ../code/core/header.cpp:340 msgid "Opens the Gobby manual" msgstr "" #: ../code/core/header.cpp:343 msgid "_About..." msgstr "" #: ../code/core/header.cpp:344 msgid "Shows Gobby's copyright and credits" msgstr "" #: ../code/core/iconmanager.cpp:49 msgid "User list" msgstr "" #: ../code/core/iconmanager.cpp:55 msgid "Document list" msgstr "" #: ../code/core/iconmanager.cpp:61 ../code/window.cpp:52 msgid "Chat" msgstr "" #: ../code/core/iconmanager.cpp:68 msgid "User Color Indicator" msgstr "" #: ../code/core/selfhoster.cpp:103 msgid "Generating 2048-bit Diffie-Hellman parameters..." msgstr "" #: ../code/core/selfhoster.cpp:135 msgid "Failed to generate Diffie-Hellman parameters" msgstr "" #: ../code/core/selfhoster.cpp:137 msgid "" "This means that Perfect Forward Secrecy (PFS) is not available. Restart " "Gobby to re-try generating the parameters. The specific error was:\n" "\n" "%1" msgstr "" #: ../code/core/selfhoster.cpp:262 msgid "" "In order to start sharing your documents, choose a private key and " "certificate or create a new pair in the preferences" msgstr "" #: ../code/core/selfhoster.cpp:282 msgid "Failed to share documents" msgstr "" #: ../code/core/sessionuserview.cpp:39 msgid "User List" msgstr "" #: ../code/core/statusbar.cpp:418 msgid "Ln %1, Col %2\t%3" msgstr "" #: ../code/core/statusbar.cpp:421 msgid "OVR" msgstr "" #: ../code/core/statusbar.cpp:421 msgid "INS" msgstr "" #: ../code/core/textsessionview.cpp:638 msgid "Text written by %1" msgstr "" #: ../code/core/textsessionview.cpp:644 msgid "Unowned text" msgstr "" #: ../code/core/userlist.cpp:108 msgid "Users" msgstr "" #: ../code/dialogs/connection-dialog.cpp:26 msgid "Connect to Server" msgstr "" #: ../code/dialogs/connection-dialog.cpp:30 msgid "Please enter a host name with which to establish a connection." msgstr "" #: ../code/dialogs/connection-dialog.cpp:32 msgid "_Remote Endpoint:" msgstr "" #: ../code/dialogs/document-location-dialog.cpp:28 msgid "Document Name:" msgstr "" #: ../code/dialogs/document-location-dialog.cpp:155 msgid "Select document's target location" msgstr "" #: ../code/dialogs/document-location-dialog.cpp:157 msgid "Choose a directory to create the document into:" msgstr "" #: ../code/dialogs/document-location-dialog.cpp:163 msgid "Select documents' target location" msgstr "" #: ../code/dialogs/document-location-dialog.cpp:165 msgid "Choose a directory to create the documents into:" msgstr "" #: ../code/dialogs/find-dialog.cpp:44 ../code/dialogs/find-dialog.cpp:142 msgid "Find" msgstr "" #: ../code/dialogs/find-dialog.cpp:47 msgid "_Search for:" msgstr "" #: ../code/dialogs/find-dialog.cpp:49 msgid "Replace _with:" msgstr "" #: ../code/dialogs/find-dialog.cpp:51 msgid "_Match case" msgstr "" #: ../code/dialogs/find-dialog.cpp:52 msgid "Match _entire word only" msgstr "" #: ../code/dialogs/find-dialog.cpp:53 msgid "Search _backwards" msgstr "" #: ../code/dialogs/find-dialog.cpp:54 msgid "Wra_p around" msgstr "" #: ../code/dialogs/find-dialog.cpp:100 msgid "Replace _All" msgstr "" #: ../code/dialogs/find-dialog.cpp:102 msgid "_Replace" msgstr "" #: ../code/dialogs/find-dialog.cpp:142 msgid "Replace" msgstr "" #: ../code/dialogs/find-dialog.cpp:161 ../code/dialogs/find-dialog.cpp:177 msgid "Phrase \"%1\" has not been found" msgstr "" #: ../code/dialogs/find-dialog.cpp:335 msgid "No occurrence has been replaced" msgstr "" #: ../code/dialogs/find-dialog.cpp:341 msgid "%1 occurrence has been replaced" msgid_plural "%1 occurrences have been replaced" msgstr[0] "" msgstr[1] "" #: ../code/dialogs/goto-dialog.cpp:26 msgid "Go to line" msgstr "" #: ../code/dialogs/goto-dialog.cpp:29 msgid "Line _number:" msgstr "" #: ../code/dialogs/goto-dialog.cpp:50 msgid "Go To _Line" msgstr "" #: ../code/dialogs/initial-dialog.cpp:76 msgid "Generating 2048-bit RSA private key..." msgstr "" #: ../code/dialogs/initial-dialog.cpp:99 msgid "Failed to generate private key" msgstr "" #: ../code/dialogs/initial-dialog.cpp:102 msgid "" "%1\n" "\n" "You can try again to create a key in the Security tab of the preferences " "dialog." msgstr "" #: ../code/dialogs/initial-dialog.cpp:141 msgid "Failed to generate self-signed certificate" msgstr "" #: ../code/dialogs/initial-dialog.cpp:144 msgid "" "%1\n" "\n" "You can try again to create a certificate in the Security tab of the " "preferences dialog." msgstr "" #: ../code/dialogs/initial-dialog.cpp:198 msgid "Choose a user color" msgstr "" #: ../code/dialogs/initial-dialog.cpp:204 msgid "Welcome to Gobby" msgstr "" #: ../code/dialogs/initial-dialog.cpp:216 msgid "" "Before we start, a few options need to be configured. You can later change " "them by choosing Edit ▸ Preferences from the menu." msgstr "" #: ../code/dialogs/initial-dialog.cpp:224 msgid "User Name" msgstr "" #: ../code/dialogs/initial-dialog.cpp:226 msgid "Your name as shown to other users." msgstr "" #: ../code/dialogs/initial-dialog.cpp:235 msgid "User Color" msgstr "" #: ../code/dialogs/initial-dialog.cpp:237 msgid "The color with which text you have written is branded." msgstr "" #: ../code/dialogs/initial-dialog.cpp:256 msgid "Remote Connections" msgstr "" #: ../code/dialogs/initial-dialog.cpp:262 #: ../code/dialogs/preferences-dialog.cpp:217 msgid "Allow remote users to edit local documents" msgstr "" #: ../code/dialogs/initial-dialog.cpp:268 msgid "Ask remote users for a password" msgstr "" #: ../code/dialogs/initial-dialog.cpp:273 #: ../code/dialogs/preferences-dialog.cpp:220 msgid "Password:" msgstr "" #: ../code/dialogs/initial-dialog.cpp:297 msgid "No authentication (Not recommended)" msgstr "" #: ../code/dialogs/initial-dialog.cpp:304 msgid "" "Don't authenticate ourselves to remote users. Data transfer will be " "unencrypted." msgstr "" #: ../code/dialogs/initial-dialog.cpp:313 msgid "Create a self-signed certificate (Recommended)" msgstr "" #: ../code/dialogs/initial-dialog.cpp:321 msgid "" "It may take a minute or two until remote users can connect while the " "security certificate is being generated." msgstr "" #: ../code/dialogs/initial-dialog.cpp:331 msgid "Use an existing certificate (Expert option)" msgstr "" #: ../code/dialogs/initial-dialog.cpp:338 msgid "" "Use an existing private key and certificate for authentication. The files " "must be in PEM format." msgstr "" #: ../code/dialogs/initial-dialog.cpp:347 #: ../code/dialogs/preferences-dialog.cpp:766 msgid "Private key:" msgstr "" #: ../code/dialogs/initial-dialog.cpp:349 #: ../code/dialogs/preferences-dialog.cpp:772 msgid "Certificate:" msgstr "" #: ../code/dialogs/open-location-dialog.cpp:26 msgid "Open Location" msgstr "" #: ../code/dialogs/open-location-dialog.cpp:27 msgid "Enter the _location (URI) of the file you would like to open:" msgstr "" #: ../code/dialogs/password-dialog.cpp:25 msgid "Password Required" msgstr "" #: ../code/dialogs/password-dialog.cpp:31 msgid "Connection to host \"%1\" requires a password." msgstr "" #: ../code/dialogs/password-dialog.cpp:32 msgid "Invalid password for host \"%1\". Please try again." msgstr "" #: ../code/dialogs/password-dialog.cpp:35 msgid "Server _Password:" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:198 msgid "Settings" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:199 msgid "Remote Users" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:200 msgid "Local Documents" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:202 msgid "User name:" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:204 msgid "User color:" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:206 msgid "Color intensity:" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:208 msgid "Choose a new user color" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:209 msgid "Show cursors of remote users" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:210 msgid "Show selections of remote users" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:212 msgid "Highlight current line of remote users" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:214 msgid "Indicate cursor position of remote users in the scrollbar" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:219 msgid "Require remote users to enter a password" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:222 msgid "Port:" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:226 msgid "Remember local documents after Gobby restart" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:227 msgid "Local documents directory:" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:423 msgid "Tab Stops" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:424 msgid "Indentation" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:425 msgid "Home/End Behavior" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:426 msgid "File Saving" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:427 msgid "_Tab width:" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:429 msgid "Insert _spaces instead of tabs" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:430 msgid "Enable automatic _indentation" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:431 msgid "Smart _home/end" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:432 msgid "Enable _automatic saving of documents" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:434 msgid "Autosave interval in _minutes:" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:455 msgid "" "With this option enabled, Home/End keys move to first/last character before " "going to the start/end of the line." msgstr "" #: ../code/dialogs/preferences-dialog.cpp:525 msgid "Text Wrapping" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:526 msgid "Line Numbers" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:527 msgid "Current Line" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:528 msgid "Right Margin" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:529 msgid "Bracket Matching" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:530 msgid "Whitespace Display" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:531 msgid "Enable text wrapping" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:532 msgid "Do not split words over two lines" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:533 msgid "Display line numbers" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:534 msgid "Highlight current line" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:535 msgid "Display right margin" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:536 msgid "Right margin at column:" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:537 msgid "Highlight matching bracket" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:589 msgid "Display no whitespace" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:592 msgid "Display spaces" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:596 msgid "Display tabs" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:600 msgid "Display tabs and spaces" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:651 msgid "Toolbar" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:652 msgid "Font" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:653 msgid "Color Scheme" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:661 msgid "Show text only" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:663 msgid "Show icons only" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:665 msgid "Show both icons and text" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:667 msgid "Show text besides icons" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:756 msgid "Trusted CAs" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:757 msgid "Secure Connection" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:758 msgid "Authentication" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:759 msgid "Select a file containing trusted CAs" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:764 msgid "None" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:765 msgid "Use a certificate" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:767 msgid "Select a private key file" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:769 #: ../code/dialogs/preferences-dialog.cpp:776 msgid "Create New..." msgstr "" #: ../code/dialogs/preferences-dialog.cpp:773 msgid "Select a certificate file" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:801 msgid "Use TLS if possible" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:804 msgid "Always use TLS" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:892 msgid "Error reading file:" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:916 msgid "2048-bit RSA private key is being generated, please wait..." msgstr "" #: ../code/dialogs/preferences-dialog.cpp:948 msgid "Select a location for the generated key" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:966 msgid "Select a location for the generated certificate" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:1068 msgid "Preferences" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:1073 msgid "User" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:1074 msgid "Editor" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:1075 msgid "View" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:1076 msgid "Appearance" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:1077 msgid "Security" msgstr "" #: ../code/main.cpp:194 msgid "Gobby options" msgstr "" #: ../code/main.cpp:194 msgid "Options related to Gobby" msgstr "" #: ../code/main.cpp:199 msgid "Display version information and exit" msgstr "" #: ../code/main.cpp:206 msgid "Also start a new Gobby instance when there is one running already" msgstr "" #: ../code/main.cpp:214 msgid "Connect to given host on startup, can be given multiple times" msgstr "" #: ../code/main.cpp:215 msgid "HOSTNAME" msgstr "" #: ../code/operations/operation-delete.cpp:65 msgid "Removing node \"%1\"..." msgstr "" #: ../code/operations/operation-delete.cpp:75 msgid "Failed to delete node \"%1\"" msgstr "" #: ../code/operations/operation-export-html.cpp:168 #, c-format msgid "written by: %s" msgstr "" #: ../code/operations/operation-export-html.cpp:241 msgid "" msgstr "" #. %1$s is session name/hostname #. %2$s is path within the session #. %3$s is current date as formatted by %c, #. %4$s is a link to the gobby site, it must be present because #. we need to handle that manually to insert a hyperlink #. instead of just printf'ing it. #: ../code/operations/operation-export-html.cpp:254 #, c-format msgid "Document generated from %1$s:%2$s at %3$s by %4$s" msgstr "" #: ../code/operations/operation-export-html.cpp:395 msgid "Participants" msgstr "" #: ../code/operations/operation-export-html.cpp:475 msgid "Exporting document \"%1\" to \"%2\" in HTML..." msgstr "" #: ../code/operations/operation-export-html.cpp:536 msgid "Failed to export document \"%1\" to HTML" msgstr "" #: ../code/operations/operation-new.cpp:75 msgid "Creating directory \"%1\"..." msgstr "" #: ../code/operations/operation-new.cpp:76 msgid "Creating document \"%1\"..." msgstr "" #: ../code/operations/operation-new.cpp:88 msgid "Failed to create directory \"%1\"" msgstr "" #: ../code/operations/operation-new.cpp:89 msgid "Failed to create document \"%1\"" msgstr "" #. Translators: This is the 8 bit encoding that is tried when #. autodetecting a file's encoding. #: ../code/operations/operation-open.cpp:38 msgid "ISO-8859-1" msgstr "" #: ../code/operations/operation-open.cpp:114 msgid "Opening document \"%1\"..." msgstr "" #: ../code/operations/operation-open.cpp:134 msgid "" "The directory into which the new document was supposed to be inserted has " "been removed" msgstr "" #: ../code/operations/operation-open.cpp:323 msgid "The file contains data not in the specified encoding" msgstr "" #: ../code/operations/operation-open.cpp:334 msgid "" "The file either contains data in an unknown encoding, or it contains binary " "data." msgstr "" #: ../code/operations/operation-open-multiple.cpp:91 msgid "Parent folder was removed" msgstr "" #: ../code/operations/operation-open-multiple.cpp:193 msgid "Failed to open multiple documents" msgstr "" #: ../code/operations/operation-save.cpp:97 msgid "Saving document \"%1\" to \"%2\"..." msgstr "" #: ../code/operations/operation-save.cpp:226 msgid "" "The document contains one or more characters that cannot be encoded in the " "specified character coding." msgstr "" #: ../code/operations/operation-save.cpp:287 msgid "Failed to save document \"%1\"" msgstr "" #: ../code/operations/operation-subscribe-path.cpp:33 msgid "Invalid path: \"%1\"" msgstr "" #: ../code/operations/operation-subscribe-path.cpp:45 msgid "Invalid path component: \"%1\"" msgstr "" #: ../code/operations/operation-subscribe-path.cpp:135 #: ../code/operations/operation-subscribe-path.cpp:200 msgid "Subscribing to \"%1\"..." msgstr "" #: ../code/operations/operation-subscribe-path.cpp:149 #: ../code/operations/operation-subscribe-path.cpp:212 msgid "Failed to connect to \"%1\"" msgstr "" #: ../code/operations/operation-subscribe-path.cpp:164 msgid "URI scheme \"%1\" not supported" msgstr "" #: ../code/operations/operation-subscribe-path.cpp:193 msgid "Connecting to \"%1\"..." msgstr "" #: ../code/operations/operation-subscribe-path.cpp:220 #: ../code/operations/operation-subscribe-path.cpp:319 #: ../code/operations/operation-subscribe-path.cpp:356 #: ../code/operations/operation-subscribe-path.cpp:452 #: ../code/operations/operation-subscribe-path.cpp:472 msgid "Could not subscribe to \"%1\"" msgstr "" #: ../code/operations/operation-subscribe-path.cpp:322 #: ../code/operations/operation-subscribe-path.cpp:358 msgid "Path \"%1\" does not exist" msgstr "" #: ../code/util/file.cpp:65 ../code/util/file.cpp:74 msgid "Could not create directory \"%1\": %2" msgstr "" #: ../code/util/uri.cpp:91 msgid "Device \"%1\" does not exist" msgstr "" #: ../code/window.cpp:104 msgid "This Computer" msgstr "" #: ../code/window.cpp:129 msgid "Document Browser" msgstr "" #: ../code/window.cpp:182 msgid "You are still connected to a session" msgstr "" #: ../code/window.cpp:190 msgid "Do you want to close Gobby nevertheless?" msgstr "" #: ../code/window.cpp:196 msgid "C_ancel" msgstr "" #: ../gobby-0.5.desktop.in.h:1 msgid "Gobby" msgstr "" #: ../gobby-0.5.desktop.in.h:2 msgid "Collaborative Editor" msgstr "" #: ../gobby-0.5.desktop.in.h:3 msgid "Gobby Collaborative Editor (0.5)" msgstr "" #: ../gobby-0.5.desktop.in.h:4 msgid "Edit text files collaboratively" msgstr "" po/it_IT.po000066400000000000000000001147271304552132100130540ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2016-10-27 22:29+0200\n" "PO-Revision-Date: 2016-10-27 23:27+0200\n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Poedit 1.8.9\n" "Last-Translator: \n" "Language: it_IT\n" #: ../code/commands/auth-commands.cpp:196 msgid "Password required" msgstr "È richiesta una password" #: ../code/commands/auth-commands.cpp:214 msgid "Incorrect password" msgstr "Password non corretta" #: ../code/commands/browser-commands.cpp:288 msgid "Subscribing to %1..." msgstr "Iscrizione a %1..." #: ../code/commands/browser-commands.cpp:301 msgid "Subscribing to chat on %1..." msgstr "Iscrizione alla chat su %1..." #: ../code/commands/browser-commands.cpp:591 msgid "Subscription failed" msgstr "Iscrizione fallita" #: ../code/commands/browser-context-commands.cpp:86 msgid "Could not find a location where to store the certificate" msgstr "Impossibile trovare una posizione dove salvare il certificato" #: ../code/commands/browser-context-commands.cpp:153 msgid "_Remove" msgstr "_Rimuovi" #: ../code/commands/browser-context-commands.cpp:188 msgid "_Disconnect from Server" msgstr "_Disconnetti dal Server" #: ../code/commands/browser-context-commands.cpp:220 msgid "Create _Account..." msgstr "crea _Account..." #: ../code/commands/browser-context-commands.cpp:245 msgid "Create Do_cument..." msgstr "Crea Do_cumento..." #: ../code/commands/browser-context-commands.cpp:280 msgid "Create Di_rectory..." msgstr "Crea Ca_rtella..." #: ../code/commands/browser-context-commands.cpp:293 msgid "_Open Document..." msgstr "Apri D_ocumento..." #: ../code/commands/browser-context-commands.cpp:312 msgid "_Permissions..." msgstr "_Permessi..." #: ../code/commands/browser-context-commands.cpp:324 msgid "D_elete" msgstr "_Elimina" #: ../code/commands/browser-context-commands.cpp:405 msgid "Choose a name for the directory" msgstr "Scegli un nome per la cartella" #: ../code/commands/browser-context-commands.cpp:406 msgid "Choose a name for the document" msgstr "Scegli un nome per il documento" #: ../code/commands/browser-context-commands.cpp:407 msgid "_Directory Name:" msgstr "Nome _della Cartella:" #: ../code/commands/browser-context-commands.cpp:408 msgid "_Document Name:" msgstr "Nome del _Documento:" #: ../code/commands/browser-context-commands.cpp:411 msgid "C_reate" msgstr "C_rea" #: ../code/commands/browser-context-commands.cpp:415 msgid "New Directory" msgstr "Nuova Cartella" #: ../code/commands/browser-context-commands.cpp:416 #: ../code/commands/file-tasks/task-new.cpp:36 msgid "New Document" msgstr "Nuovo Documento" #: ../code/commands/browser-context-commands.cpp:436 #: ../code/commands/file-tasks/task-open-file.cpp:22 msgid "Choose a text file to open" msgstr "Scegli un file di testo da aprire" #: ../code/commands/browser-context-commands.cpp:558 #: ../code/commands/browser-context-commands.cpp:569 msgid "Account successfully created" msgstr "Account creato con successo" #: ../code/commands/browser-context-commands.cpp:562 msgid "When re-connecting to the server, the new account will be used." msgstr "Alla riconnessione al server, sarà utilizzato il nuovo account." #: ../code/commands/browser-context-commands.cpp:573 msgid "" "The certificate has been stored at %1.\n" "\n" "To login to this account, set the certificate in Gobby's preferences and re-" "connect to the server." msgstr "" "Il certificato è stato salvato in %1.\n" "\n" "Per accedere a questo account, imposta il certificato nelle preferenze di " "Gobby e riconnettiti al server." #: ../code/commands/browser-context-commands.cpp:586 msgid "Failed to create account" msgstr "Impossibile creare l'account" #: ../code/commands/browser-context-commands.cpp:589 msgid "Could not save the certificate for the account: %1" msgstr "Impossibile salvare il certificato per l'account: %1" #: ../code/commands/file-tasks/task-export-html.cpp:27 msgid "Choose a location to export document \"%1\" to" msgstr "Scegli una posizione in cui esportare il documento \"%1\"" #: ../code/commands/file-tasks/task-open.cpp:46 msgid "Querying \"%1\"..." msgstr "Interrogazione di \"%1\"..." #: ../code/commands/file-tasks/task-open.cpp:103 #: ../code/operations/operation-open.cpp:474 #: ../code/operations/operation-open-multiple.cpp:180 msgid "Failed to open document \"%1\"" msgstr "Impossibile aprire il documento \"%1\"" #: ../code/commands/file-tasks/task-save.cpp:24 msgid "Choose a location to save document \"%1\" to" msgstr "Scegli una posizione in cui salvare il documento \"%1\"" #. Help browser could not be invoked, show an error message to the user. #: ../code/commands/help-commands.cpp:70 msgid "There was an error displaying help." msgstr "C'è stato un errore nell'aprire l'aiuto." #: ../code/commands/help-commands.cpp:93 msgid "Contributors:" msgstr "Contributors:" #: ../code/commands/help-commands.cpp:99 msgid "British English:" msgstr "Inglese:" #: ../code/commands/help-commands.cpp:101 msgid "German:" msgstr "Tedesco:" #: ../code/commands/help-commands.cpp:118 msgid "" "Permission to use, copy, modify, and/or distribute this software for any " "urpose with or without fee is hereby granted, provided that the above " "copyright notice and this permission notice appear in all copies.\n" "\n" "THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES " "WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF " "MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY " "SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES " "WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION " "OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN " "CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE." msgstr "" "Permission to use, copy, modify, and/or distribute this software for any " "urpose with or without fee is hereby granted, provided that the above " "copyright notice and this permission notice appear in all copies.\n" "\n" "THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES " "WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF " "MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY " "SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES " "WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION " "OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN " "CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE." #: ../code/commands/subscription-commands.cpp:148 msgid "" "The connection to the publisher of this document has been lost. Further " "changes to the document could not be synchronized to others anymore, " "therefore the document cannot be edited anymore.\n" "\n" "Please note also that it is possible that not all of your latest changes " "have reached the publisher before the connection was lost." msgstr "" "La connessione al proprietario del documento è stata persa. Non è più " "possibile sincronizzare le modifiche con gli altri, quindi non è più " "possibile modificare il documento.\n" "\n" "Nota: è possibile che non tutti gli ultimi cambiamenti abbiano raggiunto il " "proprietario prima che la connessione fosse persa." #: ../code/commands/synchronization-commands.cpp:30 msgid "Synchronization in progress... %1%%" msgstr "Sincronizzazione in corso... %1%%" #. Document cannot be used if an error happened #. during synchronization. #: ../code/commands/synchronization-commands.cpp:42 msgid "This document cannot be used." msgstr "Questo documento non può essere usato." #: ../code/commands/synchronization-commands.cpp:45 #: ../code/commands/user-join-commands.cpp:43 msgid "" "If you have an idea what could have caused the problem, then you may attempt " "to solve it and try again (after having closed this document). Otherwise it " "is most likely a bug in the software. In that case, please file a bug report " "at http://gobby.0x539.de/trac/newticket and provide as much information as " "you can, including what you did when the problem occurred and how to " "reproduce the problem (if possible) so that we can fix the problem in a " "later version. Thank you." msgstr "" "Se hai un'idea del problema, puoi provare a risolverlo e a riaprire questo " "documento. Altrimenti si tratta probabilmente di un bug del programma. In " "questo caso, inserisci un bug report all'indirizzo http://gobby.0x539.de/" "trac/newticket e fornisci tutte le informazioni possibili, incluso che cosa " "stavi facendo quando si è verificato il problema e come riprodurlo (se " "possibile) in modo che possiamo risolverlo in una versione successiva. " "Grazie." #: ../code/commands/synchronization-commands.cpp:59 msgid "Synchronization failed: %1" msgstr "Sincronizzazione fallita: %1" #. TODO: Adjust this for chat sessions #: ../code/commands/user-join-commands.cpp:38 msgid "" "You can still watch others editing the document, but you cannot edit it " "yourself." msgstr "" "Puoi assistere alle modifiche da parte degli altri, ma non puoi modificare " "il documento." #: ../code/commands/user-join-commands.cpp:57 msgid "User Join failed: %1" msgstr "User Join fallito: %1" #: ../code/commands/user-join-commands.cpp:64 msgid "Permissions are not granted to modify the document." msgstr "Non si dispone dei permessi per modificare il documento." #: ../code/core/browser.cpp:111 msgid "_Direct Connection" msgstr "Connessione _Diretta" #: ../code/core/browser.cpp:113 msgid "Host Name:" msgstr "Nome Host:" #: ../code/core/certificatemanager.cpp:83 #, c-format msgid "Failed to write Diffie-Hellman parameters to \"%s\": %s" msgstr "Impossibile scrivere i parametri di Diffie-Helman in \"%s\": \"%s\"" #: ../code/core/certificatemanager.cpp:276 #, c-format msgid "Failed to read Diffie-Hellman parameters: %s" msgstr "Impossibile leggere i parametri Diffie-Hellman: %s" #: ../code/core/certificatemanager.cpp:338 msgid "File does not contain a X.509 certificate" msgstr "Il file non contiene un certificato X.509" #: ../code/core/certificatemanager.cpp:414 msgid "Certificate does not belong to the chosen key" msgstr "Il certificato non appartiene alla chiave scelta" #: ../code/core/foldermanager.cpp:322 msgid "The document has been removed from the server." msgstr "Il documento è stato rimosso dal server." #: ../code/core/header.cpp:203 msgid "_None" msgstr "_Nessuno" #: ../code/core/header.cpp:227 msgid "_File" msgstr "_File" #: ../code/core/header.cpp:230 msgid "_Open..." msgstr "_Apri..." #: ../code/core/header.cpp:233 msgid "Open _Location..." msgstr "Apri _Posizione..." #: ../code/core/header.cpp:237 msgid "Save _As..." msgstr "Salva _Come..." #: ../code/core/header.cpp:241 ../code/core/iconmanager.cpp:43 msgid "Save All" msgstr "Salva Tutti" #: ../code/core/header.cpp:241 msgid "Save all open files locally" msgstr "Salva localmente tutti i file aperti" #: ../code/core/header.cpp:243 msgid "Export As _HTML..." msgstr "Esporta come _HTML..." #: ../code/core/header.cpp:246 msgid "Connect _to Server..." msgstr "Conne_tti al Server..." #: ../code/core/header.cpp:251 msgid "_Edit" msgstr "_Modifica" #: ../code/core/header.cpp:259 msgid "_Find..." msgstr "_Trova.." #: ../code/core/header.cpp:261 msgid "Find Ne_xt" msgstr "Trova P_rossimo" #: ../code/core/header.cpp:262 msgid "Find next match of phrase searched for" msgstr "Trova la prossima corrispondenza della frase cercata" #: ../code/core/header.cpp:265 msgid "Find Pre_vious" msgstr "Trova Precede_nte" #: ../code/core/header.cpp:266 msgid "Find previous match of phrase searched for" msgstr "Trova corrispondenza precedente della frase cercata" #: ../code/core/header.cpp:271 msgid "Find and Rep_lace..." msgstr "Trova e Sos_tituisci" #: ../code/core/header.cpp:275 msgid "Go to _Line..." msgstr "Vai alla _Linea..." #: ../code/core/header.cpp:279 msgid "Pr_eferences..." msgstr "Pr_eferenze..." #: ../code/core/header.cpp:281 msgid "_View" msgstr "_Visualizza" #: ../code/core/header.cpp:284 msgid "Reset User Colors" msgstr "Reimposta i colori degli utenti" #: ../code/core/header.cpp:285 msgid "Hide user colors in current document" msgstr "Nascondi i colori degli utenti" #: ../code/core/header.cpp:289 msgid "Fullscreen" msgstr "Schermo intero" #: ../code/core/header.cpp:290 msgid "Show the editing window in fullscreen mode" msgstr "Mostra la finestra di modifica a schermo intero" #: ../code/core/header.cpp:294 msgid "Zoom In" msgstr "Ingrandisci" #: ../code/core/header.cpp:297 msgid "Zoom Out" msgstr "Rimpicciolisci" #: ../code/core/header.cpp:300 msgid "View Toolbar" msgstr "Visualizza Barra degli Strumenti" #: ../code/core/header.cpp:301 msgid "Whether to show the toolbar" msgstr "Stabilisce se mostrare la barra degli strumenti" #: ../code/core/header.cpp:305 msgid "View Statusbar" msgstr "Visualizza la Barra di Stato" #: ../code/core/header.cpp:306 msgid "Whether to show the statusbar" msgstr "Stabilisce se visualizzare la barra di stato" #: ../code/core/header.cpp:310 msgid "View Document Browser" msgstr "Visualizza Browser dei Documenti" #: ../code/core/header.cpp:311 msgid "Whether to show the document browser" msgstr "Stabilisce se visualizzare il browser dei documenti" #: ../code/core/header.cpp:315 msgid "View Chat" msgstr "Visualizza Chat" #: ../code/core/header.cpp:316 msgid "Whether to show the chat pane" msgstr "Stabilisce se visualizzare il pannello della chat" #: ../code/core/header.cpp:320 msgid "View Document User List" msgstr "Visualizza Lista Utenti del Documento" #: ../code/core/header.cpp:321 msgid "Whether to show the user list for documents" msgstr "Stabilisce se visualizzare la lista di utenti del documento" #: ../code/core/header.cpp:325 msgid "View Chat User List" msgstr "Visualizza Lista di utenti della chat" #: ../code/core/header.cpp:326 msgid "Whether to show the user list for the chat" msgstr "Stabilisce se visiualizzare la lista degli utenti della chat" #: ../code/core/header.cpp:330 msgid "_Highlight Mode" msgstr "Modalità _Evidenziazione" #: ../code/core/header.cpp:336 msgid "_Help" msgstr "_Aiuto" #: ../code/core/header.cpp:339 msgid "_Contents" msgstr "_Contenuti" #: ../code/core/header.cpp:340 msgid "Opens the Gobby manual" msgstr "Apri il manuale di Gobby" #: ../code/core/header.cpp:343 msgid "_About..." msgstr "Inform_azioni..." #: ../code/core/header.cpp:344 msgid "Shows Gobby's copyright and credits" msgstr "Mostra i riconoscimenti e il copyright di Gobby" #: ../code/core/iconmanager.cpp:49 msgid "User list" msgstr "Lista utenti" #: ../code/core/iconmanager.cpp:55 msgid "Document list" msgstr "Lista documenti" #: ../code/core/iconmanager.cpp:61 ../code/window.cpp:52 msgid "Chat" msgstr "Chat" #: ../code/core/iconmanager.cpp:68 msgid "User Color Indicator" msgstr "Indicatore colore dell'utente" #: ../code/core/selfhoster.cpp:103 msgid "Generating 2048-bit Diffie-Hellman parameters..." msgstr "Generazione dei parametri Diffie-Hellman a 2048-bit" #: ../code/core/selfhoster.cpp:135 msgid "Failed to generate Diffie-Hellman parameters" msgstr "Impossibile generare i parametri Diffie-Hellman" #: ../code/core/selfhoster.cpp:137 msgid "" "This means that Perfect Forward Secrecy (PFS) is not available. Restart " "Gobby to re-try generating the parameters. The specific error was:\n" "\n" "%1" msgstr "" "Questo significa che la Perfect Forward Secrecy (PFS) non è disponibile. " "Riavvia Gobby per provare a rigenerare i parametri. L'errore specifico è: \n" "\n" "%1" #: ../code/core/selfhoster.cpp:262 msgid "" "In order to start sharing your documents, choose a private key and " "certificate or create a new pair in the preferences" msgstr "" "Per condividere i tuoi documenti, scegli una chiave privata e un certificato " "o crea una nuova coppia di chiavi nelle preferenze" #: ../code/core/selfhoster.cpp:282 msgid "Failed to share documents" msgstr "Impossibile condividere i documenti" #: ../code/core/sessionuserview.cpp:39 msgid "User List" msgstr "Lista Utenti" #: ../code/core/statusbar.cpp:418 msgid "Ln %1, Col %2\t%3" msgstr "Ln %1, Col %2\t%3" #: ../code/core/statusbar.cpp:421 msgid "OVR" msgstr "SSC" #: ../code/core/statusbar.cpp:421 msgid "INS" msgstr "INS" #: ../code/core/textsessionview.cpp:638 msgid "Text written by %1" msgstr "Testo di %1" #: ../code/core/textsessionview.cpp:644 msgid "Unowned text" msgstr "Testo senza proprietario" #: ../code/core/userlist.cpp:108 msgid "Users" msgstr "Utenti" #: ../code/dialogs/connection-dialog.cpp:26 msgid "Connect to Server" msgstr "Connetti al server" #: ../code/dialogs/connection-dialog.cpp:30 msgid "Please enter a host name with which to establish a connection." msgstr "Inserisci un nome host con cui stabilire una connessione." #: ../code/dialogs/connection-dialog.cpp:32 msgid "_Remote Endpoint:" msgstr "Endpoint _remoto:" #: ../code/dialogs/document-location-dialog.cpp:28 msgid "Document Name:" msgstr "Nome Documento:" #: ../code/dialogs/document-location-dialog.cpp:155 msgid "Select document's target location" msgstr "Scegli la destinazione del documento" #: ../code/dialogs/document-location-dialog.cpp:157 msgid "Choose a directory to create the document into:" msgstr "Scegli una cartella in cui creare il documento:" #: ../code/dialogs/document-location-dialog.cpp:163 msgid "Select documents' target location" msgstr "Scegli la destinazione del documento" #: ../code/dialogs/document-location-dialog.cpp:165 msgid "Choose a directory to create the documents into:" msgstr "Choose a directory to create the document into:" #: ../code/dialogs/find-dialog.cpp:44 ../code/dialogs/find-dialog.cpp:142 msgid "Find" msgstr "Trova" #: ../code/dialogs/find-dialog.cpp:47 msgid "_Search for:" msgstr "_Cerca:" #: ../code/dialogs/find-dialog.cpp:49 msgid "Replace _with:" msgstr "Sostituisci _con" #: ../code/dialogs/find-dialog.cpp:51 msgid "_Match case" msgstr "Corrispondenza _maiuscole/minuscole" #: ../code/dialogs/find-dialog.cpp:52 msgid "Match _entire word only" msgstr "Solo parole int_ere" #: ../code/dialogs/find-dialog.cpp:53 msgid "Search _backwards" msgstr "Cerca all'indietro" #: ../code/dialogs/find-dialog.cpp:54 msgid "Wra_p around" msgstr "Ricomincia da ca_po" #: ../code/dialogs/find-dialog.cpp:100 msgid "Replace _All" msgstr "Sostituisci _Tutto" #: ../code/dialogs/find-dialog.cpp:102 msgid "_Replace" msgstr "Sostituis_ci" #: ../code/dialogs/find-dialog.cpp:142 msgid "Replace" msgstr "Sostituisci" #: ../code/dialogs/find-dialog.cpp:161 ../code/dialogs/find-dialog.cpp:177 msgid "Phrase \"%1\" has not been found" msgstr "La frase \"%1\" non è stata trovata" #: ../code/dialogs/find-dialog.cpp:335 msgid "No occurrence has been replaced" msgstr "Nessuna occorrenza sostituita" #: ../code/dialogs/find-dialog.cpp:341 msgid "%1 occurrence has been replaced" msgid_plural "%1 occurrences have been replaced" msgstr[0] "%1 occorrenza è stata sostituita" msgstr[1] "%1 occorrenze sono state sostituite" #: ../code/dialogs/goto-dialog.cpp:26 msgid "Go to line" msgstr "Vai alla riga" #: ../code/dialogs/goto-dialog.cpp:29 msgid "Line _number:" msgstr "_Numero riga:" #: ../code/dialogs/goto-dialog.cpp:50 msgid "Go To _Line" msgstr "Vai alla _Riga" #: ../code/dialogs/initial-dialog.cpp:76 msgid "Generating 2048-bit RSA private key..." msgstr "Generazione chiave privata RSA a 2048-bit" #: ../code/dialogs/initial-dialog.cpp:99 msgid "Failed to generate private key" msgstr "Impossibile generare la chiave privata" #: ../code/dialogs/initial-dialog.cpp:102 msgid "" "%1\n" "\n" "You can try again to create a key in the Security tab of the preferences " "dialog." msgstr "" "%1\n" "\n" "Puoi provare ancora a creare una chiave nella scheda Sicurezza delle " "Preferenze" #: ../code/dialogs/initial-dialog.cpp:141 msgid "Failed to generate self-signed certificate" msgstr "Impossibile generare il certificato auto-firmato" #: ../code/dialogs/initial-dialog.cpp:144 msgid "" "%1\n" "\n" "You can try again to create a certificate in the Security tab of the " "preferences dialog." msgstr "" "%1\n" "\n" "Puoi provare ancora a creare un certificato nella scheda Sicurezza delle " "Preferenze" #: ../code/dialogs/initial-dialog.cpp:198 msgid "Choose a user color" msgstr "Scegli un colore utente" #: ../code/dialogs/initial-dialog.cpp:204 msgid "Welcome to Gobby" msgstr "Benvenuto in Gobby" #: ../code/dialogs/initial-dialog.cpp:216 msgid "" "Before we start, a few options need to be configured. You can later change " "them by choosing Edit ▸ Preferences from the menu." msgstr "" "Prima di iniziare, occorre configurare qualche opzione. Puoi modificarle più " "tardi scegliendo Modifica ▸ Preferenze dal menu." #: ../code/dialogs/initial-dialog.cpp:224 msgid "User Name" msgstr "Nome Utente" #: ../code/dialogs/initial-dialog.cpp:226 msgid "Your name as shown to other users." msgstr "Il tuo nome come mostrato agli altri" #: ../code/dialogs/initial-dialog.cpp:235 msgid "User Color" msgstr "Colore utente" #: ../code/dialogs/initial-dialog.cpp:237 msgid "The color with which text you have written is branded." msgstr "Il colore con cui è marcato il testo scritto da te." #: ../code/dialogs/initial-dialog.cpp:256 msgid "Remote Connections" msgstr "Permetti connessioni remote" #: ../code/dialogs/initial-dialog.cpp:262 #: ../code/dialogs/preferences-dialog.cpp:217 msgid "Allow remote users to edit local documents" msgstr "Permetti agli utenti remoti di modificare i documenti locali" #: ../code/dialogs/initial-dialog.cpp:268 msgid "Ask remote users for a password" msgstr "Richiedi una password agli utenti remoti" #: ../code/dialogs/initial-dialog.cpp:273 #: ../code/dialogs/preferences-dialog.cpp:220 msgid "Password:" msgstr "Password:" #: ../code/dialogs/initial-dialog.cpp:297 msgid "No authentication (Not recommended)" msgstr "Nessuna autenticazione (sconsigliato)" #: ../code/dialogs/initial-dialog.cpp:304 msgid "" "Don't authenticate ourselves to remote users. Data transfer will be " "unencrypted." msgstr "" "Non autenticarmi agli utenti remoti. I dati saranno trasmessi in chiaro." #: ../code/dialogs/initial-dialog.cpp:313 msgid "Create a self-signed certificate (Recommended)" msgstr "Crea un certificato auto-firmato (consigliato)" #: ../code/dialogs/initial-dialog.cpp:321 msgid "" "It may take a minute or two until remote users can connect while the " "security certificate is being generated." msgstr "" "Potrebbero volerci due minuti prima che gli utenti remoti possano " "connettersi, mentre il certificato viene generato." #: ../code/dialogs/initial-dialog.cpp:331 msgid "Use an existing certificate (Expert option)" msgstr "Usa un certificato esistente (per esperti)" #: ../code/dialogs/initial-dialog.cpp:338 msgid "" "Use an existing private key and certificate for authentication. The files " "must be in PEM format." msgstr "" "Usa una chiave privata e un certificato esistenti. I file devono essere nel " "formato PEM." #: ../code/dialogs/initial-dialog.cpp:347 #: ../code/dialogs/preferences-dialog.cpp:766 msgid "Private key:" msgstr "Chiave privata:" #: ../code/dialogs/initial-dialog.cpp:349 #: ../code/dialogs/preferences-dialog.cpp:772 msgid "Certificate:" msgstr "Certificato:" #: ../code/dialogs/open-location-dialog.cpp:26 msgid "Open Location" msgstr "Apri Posizione" #: ../code/dialogs/open-location-dialog.cpp:27 msgid "Enter the _location (URI) of the file you would like to open:" msgstr "Inserisci la _posizione (URI) del file che vuoi aprire:" #: ../code/dialogs/password-dialog.cpp:25 msgid "Password Required" msgstr "È richiesta una password" #: ../code/dialogs/password-dialog.cpp:31 msgid "Connection to host \"%1\" requires a password." msgstr "La connessione all'host \"%1\" richiede una password." #: ../code/dialogs/password-dialog.cpp:32 msgid "Invalid password for host \"%1\". Please try again." msgstr "Password non valida per l'host \"%1\". Riprova." #: ../code/dialogs/password-dialog.cpp:35 msgid "Server _Password:" msgstr "_Password del server:" #: ../code/dialogs/preferences-dialog.cpp:198 msgid "Settings" msgstr "Impostazioni" #: ../code/dialogs/preferences-dialog.cpp:199 msgid "Remote Users" msgstr "Utenti remoti" #: ../code/dialogs/preferences-dialog.cpp:200 msgid "Local Documents" msgstr "Documenti locali" #: ../code/dialogs/preferences-dialog.cpp:202 msgid "User name:" msgstr "Nome utente:" #: ../code/dialogs/preferences-dialog.cpp:204 msgid "User color:" msgstr "Colore utente:" #: ../code/dialogs/preferences-dialog.cpp:206 msgid "Color intensity:" msgstr "Intensità colore:" #: ../code/dialogs/preferences-dialog.cpp:208 msgid "Choose a new user color" msgstr "Scegli un nuovo colore utente" #: ../code/dialogs/preferences-dialog.cpp:209 msgid "Show cursors of remote users" msgstr "Mostra il cursore degli utenti remoti" #: ../code/dialogs/preferences-dialog.cpp:210 msgid "Show selections of remote users" msgstr "Mostra la selezione degli utenti remoti" #: ../code/dialogs/preferences-dialog.cpp:212 msgid "Highlight current line of remote users" msgstr "Evidenzia la linea corrente degli utenti remoti" #: ../code/dialogs/preferences-dialog.cpp:214 msgid "Indicate cursor position of remote users in the scrollbar" msgstr "" "Mostra la posizione del cursore degli utenti remoti sulla barra di " "scorrimento" #: ../code/dialogs/preferences-dialog.cpp:219 msgid "Require remote users to enter a password" msgstr "Richiedi che gli utenti remoti inseriscano una password" #: ../code/dialogs/preferences-dialog.cpp:222 msgid "Port:" msgstr "Porta:" #: ../code/dialogs/preferences-dialog.cpp:226 msgid "Remember local documents after Gobby restart" msgstr "Ricorda i documenti locali dopo il riavvio di Gobby" #: ../code/dialogs/preferences-dialog.cpp:227 msgid "Local documents directory:" msgstr "Cartella dei documenti locali:" #: ../code/dialogs/preferences-dialog.cpp:423 msgid "Tab Stops" msgstr "Tabulazioni" #: ../code/dialogs/preferences-dialog.cpp:424 msgid "Indentation" msgstr "Indentazione" #: ../code/dialogs/preferences-dialog.cpp:425 msgid "Home/End Behavior" msgstr "Comportamento tasti Inizio/Fine" #: ../code/dialogs/preferences-dialog.cpp:426 msgid "File Saving" msgstr "Salvataggio file" #: ../code/dialogs/preferences-dialog.cpp:427 msgid "_Tab width:" msgstr "Larghezza _tab" #: ../code/dialogs/preferences-dialog.cpp:429 msgid "Insert _spaces instead of tabs" msgstr "Inserisci _spazi al posto delle tabulazioni" #: ../code/dialogs/preferences-dialog.cpp:430 msgid "Enable automatic _indentation" msgstr "Abilita _indentazione automatica" #: ../code/dialogs/preferences-dialog.cpp:431 msgid "Smart _home/end" msgstr "Inizio/_fine intelligente" #: ../code/dialogs/preferences-dialog.cpp:432 msgid "Enable _automatic saving of documents" msgstr "Abilita salvataggio _automatico dei documenti" #: ../code/dialogs/preferences-dialog.cpp:434 msgid "Autosave interval in _minutes:" msgstr "Ogni quanti minuti salvare automaticamente: " #: ../code/dialogs/preferences-dialog.cpp:455 msgid "" "With this option enabled, Home/End keys move to first/last character before " "going to the start/end of the line." msgstr "" "Con questa opzione abilitata, i tasti Inizio/Fine muovono il cursore al " "primo/ultimo carattere prima di andare all'inizio/fine della riga." #: ../code/dialogs/preferences-dialog.cpp:525 msgid "Text Wrapping" msgstr "A capo automatico" #: ../code/dialogs/preferences-dialog.cpp:526 msgid "Line Numbers" msgstr "Numeri di linea" #: ../code/dialogs/preferences-dialog.cpp:527 msgid "Current Line" msgstr "Linea corrente" #: ../code/dialogs/preferences-dialog.cpp:528 msgid "Right Margin" msgstr "Margine destro" #: ../code/dialogs/preferences-dialog.cpp:529 msgid "Bracket Matching" msgstr "Parentesi" #: ../code/dialogs/preferences-dialog.cpp:530 msgid "Whitespace Display" msgstr "Mostra caratteri non stampabili" #: ../code/dialogs/preferences-dialog.cpp:531 msgid "Enable text wrapping" msgstr "Abilita a capo automatico" #: ../code/dialogs/preferences-dialog.cpp:532 msgid "Do not split words over two lines" msgstr "Non dividere le parole su due righe" #: ../code/dialogs/preferences-dialog.cpp:533 msgid "Display line numbers" msgstr "Mostra numeri di riga" #: ../code/dialogs/preferences-dialog.cpp:534 msgid "Highlight current line" msgstr "Evidenzia riga corrente" #: ../code/dialogs/preferences-dialog.cpp:535 msgid "Display right margin" msgstr "Mostra margine destro" #: ../code/dialogs/preferences-dialog.cpp:536 msgid "Right margin at column:" msgstr "Margine destro alla colonna:" #: ../code/dialogs/preferences-dialog.cpp:537 msgid "Highlight matching bracket" msgstr "Evidenzia parentesi corrispondenti" #: ../code/dialogs/preferences-dialog.cpp:589 msgid "Display no whitespace" msgstr "Non mostrare caratteri non stampabili" #: ../code/dialogs/preferences-dialog.cpp:592 msgid "Display spaces" msgstr "Mostra spazi" #: ../code/dialogs/preferences-dialog.cpp:596 msgid "Display tabs" msgstr "Mostra tabulazioni" #: ../code/dialogs/preferences-dialog.cpp:600 msgid "Display tabs and spaces" msgstr "Mostra spazi e tabulazioni" #: ../code/dialogs/preferences-dialog.cpp:651 msgid "Toolbar" msgstr "Barra degli strumenti" #: ../code/dialogs/preferences-dialog.cpp:652 msgid "Font" msgstr "Carattere" #: ../code/dialogs/preferences-dialog.cpp:653 msgid "Color Scheme" msgstr "Tema dei Colori" #: ../code/dialogs/preferences-dialog.cpp:661 msgid "Show text only" msgstr "Solo testo" #: ../code/dialogs/preferences-dialog.cpp:663 msgid "Show icons only" msgstr "Solo icone" #: ../code/dialogs/preferences-dialog.cpp:665 msgid "Show both icons and text" msgstr "Icone e testo" #: ../code/dialogs/preferences-dialog.cpp:667 msgid "Show text besides icons" msgstr "Testo accanto alle icone" #: ../code/dialogs/preferences-dialog.cpp:756 msgid "Trusted CAs" msgstr "Certificate Autorities fidate" #: ../code/dialogs/preferences-dialog.cpp:757 msgid "Secure Connection" msgstr "Connessione Sicura" #: ../code/dialogs/preferences-dialog.cpp:758 msgid "Authentication" msgstr "Autenticazione" #: ../code/dialogs/preferences-dialog.cpp:759 msgid "Select a file containing trusted CAs" msgstr "Scegli file contenente le Certificate Autorities fidate" #: ../code/dialogs/preferences-dialog.cpp:764 msgid "None" msgstr "Nessuno" #: ../code/dialogs/preferences-dialog.cpp:765 msgid "Use a certificate" msgstr "Usa certificato" #: ../code/dialogs/preferences-dialog.cpp:767 msgid "Select a private key file" msgstr "Scegli file della chiave privata" #: ../code/dialogs/preferences-dialog.cpp:769 #: ../code/dialogs/preferences-dialog.cpp:776 msgid "Create New..." msgstr "Crea Nuovo..." #: ../code/dialogs/preferences-dialog.cpp:773 msgid "Select a certificate file" msgstr "Scegli file del certificato" #: ../code/dialogs/preferences-dialog.cpp:801 msgid "Use TLS if possible" msgstr "Usa TLS se possibile" #: ../code/dialogs/preferences-dialog.cpp:804 msgid "Always use TLS" msgstr "Usa sempre TLS" #: ../code/dialogs/preferences-dialog.cpp:892 msgid "Error reading file:" msgstr "Errore nella lettura del file:" #: ../code/dialogs/preferences-dialog.cpp:916 msgid "2048-bit RSA private key is being generated, please wait..." msgstr "La chiave privata RSA a 2048-bit viene generata, attendere..." #: ../code/dialogs/preferences-dialog.cpp:948 msgid "Select a location for the generated key" msgstr "Scegli una posizione per la chiave generata" #: ../code/dialogs/preferences-dialog.cpp:966 msgid "Select a location for the generated certificate" msgstr "Scegli una posizione per il certificato generato" #: ../code/dialogs/preferences-dialog.cpp:1068 msgid "Preferences" msgstr "Preferenze" #: ../code/dialogs/preferences-dialog.cpp:1073 msgid "User" msgstr "Utente" #: ../code/dialogs/preferences-dialog.cpp:1074 msgid "Editor" msgstr "Editor" #: ../code/dialogs/preferences-dialog.cpp:1075 msgid "View" msgstr "Visualizzazione" #: ../code/dialogs/preferences-dialog.cpp:1076 msgid "Appearance" msgstr "Aspetto" #: ../code/dialogs/preferences-dialog.cpp:1077 msgid "Security" msgstr "Sicurezza" #: ../code/main.cpp:194 msgid "Gobby options" msgstr "Opzioni di Gobby" #: ../code/main.cpp:194 msgid "Options related to Gobby" msgstr "Opzioni riguardanti Gobby" #: ../code/main.cpp:199 msgid "Display version information and exit" msgstr "Mostra informazioni sulla versione ed esci" #: ../code/main.cpp:206 msgid "Also start a new Gobby instance when there is one running already" msgstr "Apri nuova istanza di Gobby se una è già presente" #: ../code/main.cpp:214 msgid "Connect to given host on startup, can be given multiple times" msgstr "All'avvio connettiti all'host fornito, può essere fornito più volte" #: ../code/main.cpp:215 msgid "HOSTNAME" msgstr "HOSTNAME" #: ../code/operations/operation-delete.cpp:65 msgid "Removing node \"%1\"..." msgstr "Rimozione nodo \"%1\"..." #: ../code/operations/operation-delete.cpp:75 msgid "Failed to delete node \"%1\"" msgstr "Impossibile rimuovere nodo \"%1\"" #: ../code/operations/operation-export-html.cpp:168 #, c-format msgid "written by: %s" msgstr "scritto da: %s" #: ../code/operations/operation-export-html.cpp:241 msgid "" msgstr "" #. %1$s is session name/hostname #. %2$s is path within the session #. %3$s is current date as formatted by %c, #. %4$s is a link to the gobby site, it must be present because #. we need to handle that manually to insert a hyperlink #. instead of just printf'ing it. #: ../code/operations/operation-export-html.cpp:254 #, c-format msgid "Document generated from %1$s:%2$s at %3$s by %4$s" msgstr "Documento generato a partire da %1$s:%2$s in data %3$s da %4$s" #: ../code/operations/operation-export-html.cpp:395 msgid "Participants" msgstr "Partecipanti" #: ../code/operations/operation-export-html.cpp:475 msgid "Exporting document \"%1\" to \"%2\" in HTML..." msgstr "Esportazione documento \"%1\" su \"%2\" in HTML..." #: ../code/operations/operation-export-html.cpp:536 msgid "Failed to export document \"%1\" to HTML" msgstr "Impossibile esportare il documento \"%1\" in HTML" #: ../code/operations/operation-new.cpp:75 msgid "Creating directory \"%1\"..." msgstr "Creazione cartella \"%1\"..." #: ../code/operations/operation-new.cpp:76 msgid "Creating document \"%1\"..." msgstr "Creazione documento \"%1\"..." #: ../code/operations/operation-new.cpp:88 msgid "Failed to create directory \"%1\"" msgstr "Impossibile creare la cartella \"%1\"" #: ../code/operations/operation-new.cpp:89 msgid "Failed to create document \"%1\"" msgstr "Impossibile creare il documento \"%1\"" #. Translators: This is the 8 bit encoding that is tried when #. autodetecting a file's encoding. #: ../code/operations/operation-open.cpp:38 msgid "ISO-8859-1" msgstr "ISO-8859-1" #: ../code/operations/operation-open.cpp:114 msgid "Opening document \"%1\"..." msgstr "Apertura documento \"%1\"..." #: ../code/operations/operation-open.cpp:134 msgid "" "The directory into which the new document was supposed to be inserted has " "been removed" msgstr "" "La cartella in cui il nuovo documento doveva essere inserito è stata rimossa" #: ../code/operations/operation-open.cpp:323 msgid "The file contains data not in the specified encoding" msgstr "Il file contiene dati che non sono nella codifica specificata" #: ../code/operations/operation-open.cpp:334 msgid "" "The file either contains data in an unknown encoding, or it contains binary " "data." msgstr "" "Il file contiene dati in una codifica non nota, oppure contiene dati binari." #: ../code/operations/operation-open-multiple.cpp:91 msgid "Parent folder was removed" msgstr "Una cartella superiore è stata rimossa" #: ../code/operations/operation-open-multiple.cpp:193 msgid "Failed to open multiple documents" msgstr "Impossibile aprire più documenti" #: ../code/operations/operation-save.cpp:97 msgid "Saving document \"%1\" to \"%2\"..." msgstr "Salvataggio documento \"%1\" su \"%2\"..." #: ../code/operations/operation-save.cpp:226 msgid "" "The document contains one or more characters that cannot be encoded in the " "specified character coding." msgstr "" "Il documento contiene uno o più caratteri che non possono essere " "rappresentati nella codifica specificata." #: ../code/operations/operation-save.cpp:287 msgid "Failed to save document \"%1\"" msgstr "Impossibile salvare il documento \"%1\"" #: ../code/operations/operation-subscribe-path.cpp:33 msgid "Invalid path: \"%1\"" msgstr "Percorso non valido: \"%1\"" #: ../code/operations/operation-subscribe-path.cpp:45 msgid "Invalid path component: \"%1\"" msgstr "Componente del percorso non valida: \"%1\"" #: ../code/operations/operation-subscribe-path.cpp:135 #: ../code/operations/operation-subscribe-path.cpp:200 msgid "Subscribing to \"%1\"..." msgstr "Iscrizione a \"%1\"..." #: ../code/operations/operation-subscribe-path.cpp:149 #: ../code/operations/operation-subscribe-path.cpp:212 msgid "Failed to connect to \"%1\"" msgstr "Impossibile connettere a \"%1\"" #: ../code/operations/operation-subscribe-path.cpp:164 msgid "URI scheme \"%1\" not supported" msgstr "Lo schema URI \"%1\" non è supportato" #: ../code/operations/operation-subscribe-path.cpp:193 msgid "Connecting to \"%1\"..." msgstr "Connessione a \"%1\"..." #: ../code/operations/operation-subscribe-path.cpp:220 #: ../code/operations/operation-subscribe-path.cpp:319 #: ../code/operations/operation-subscribe-path.cpp:356 #: ../code/operations/operation-subscribe-path.cpp:452 #: ../code/operations/operation-subscribe-path.cpp:472 msgid "Could not subscribe to \"%1\"" msgstr "Impossibile iscriversi a \"%1\"" #: ../code/operations/operation-subscribe-path.cpp:322 #: ../code/operations/operation-subscribe-path.cpp:358 msgid "Path \"%1\" does not exist" msgstr "Il percorso \"%1\" non esiste" #: ../code/util/file.cpp:65 ../code/util/file.cpp:74 msgid "Could not create directory \"%1\": %2" msgstr "Impossibile creare cartella \"%1\": %2" #: ../code/util/uri.cpp:91 msgid "Device \"%1\" does not exist" msgstr "Il dispositivo \"%1\" non esiste" #: ../code/window.cpp:104 msgid "This Computer" msgstr "Questo Computer" #: ../code/window.cpp:129 msgid "Document Browser" msgstr "Browser Documenti" #: ../code/window.cpp:182 msgid "You are still connected to a session" msgstr "Sei ancora connesso a una sessione" #: ../code/window.cpp:190 msgid "Do you want to close Gobby nevertheless?" msgstr "Vuoi ugualmente chiudere Gobby?" #: ../code/window.cpp:196 msgid "C_ancel" msgstr "Annulla" #: ../gobby-0.5.desktop.in.h:1 msgid "Gobby" msgstr "Gobby" #: ../gobby-0.5.desktop.in.h:2 msgid "Collaborative Editor" msgstr "Editor Collaborativo" #: ../gobby-0.5.desktop.in.h:3 msgid "Gobby Collaborative Editor (0.5)" msgstr "Gobby Editor Collaborativo (0.5)" #: ../gobby-0.5.desktop.in.h:4 msgid "Edit text files collaboratively" msgstr "Modifica file di testo in modo collaborativo" po/ja.po000066400000000000000000001153031304552132100124250ustar00rootroot00000000000000# Japanese translations for gobby package. # Copyright (C) 2010 THE gobby'S COPYRIGHT HOLDER # This file is distributed under the same license as the gobby package. # , 2010. # msgid "" msgstr "" "Project-Id-Version: gobby 0.4.94\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2014-08-23 13:43-0400\n" "PO-Revision-Date: 2010-11-19 01:09+0900\n" "Last-Translator: Takahiro Sunaga \n" "Language-Team: Japanese\n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" #: ../code/commands/auth-commands.cpp:196 #, fuzzy msgid "Password required" msgstr "パスワードが必要です" #: ../code/commands/auth-commands.cpp:214 msgid "Incorrect password" msgstr "" #: ../code/commands/browser-commands.cpp:288 msgid "Subscribing to %1..." msgstr "%1へ加入..." #: ../code/commands/browser-commands.cpp:301 msgid "Subscribing to chat on %1..." msgstr "チャット%1へ加入..." #: ../code/commands/browser-commands.cpp:591 msgid "Subscription failed" msgstr "加入に失敗" #: ../code/commands/browser-context-commands.cpp:86 msgid "Could not find a location where to store the certificate" msgstr "" #: ../code/commands/browser-context-commands.cpp:153 msgid "_Remove" msgstr "" #: ../code/commands/browser-context-commands.cpp:188 #, fuzzy msgid "_Disconnect from Server" msgstr "サーバーへ接続" #: ../code/commands/browser-context-commands.cpp:220 #, fuzzy msgid "Create _Account..." msgstr "ドキュメントの作成(_C)" #: ../code/commands/browser-context-commands.cpp:245 msgid "Create Do_cument..." msgstr "ドキュメントの作成(_C)" #: ../code/commands/browser-context-commands.cpp:280 #, fuzzy msgid "Create Di_rectory..." msgstr "ディレクトリの作成" #: ../code/commands/browser-context-commands.cpp:293 msgid "_Open Document..." msgstr "ドキュメントを開く(_O)" #: ../code/commands/browser-context-commands.cpp:312 msgid "_Permissions..." msgstr "" #: ../code/commands/browser-context-commands.cpp:324 msgid "D_elete" msgstr "削除(_D)" #: ../code/commands/browser-context-commands.cpp:405 msgid "Choose a name for the directory" msgstr "ディレクトリ名の選択" #: ../code/commands/browser-context-commands.cpp:406 msgid "Choose a name for the document" msgstr "ドキュメント名の選択" #: ../code/commands/browser-context-commands.cpp:407 msgid "_Directory Name:" msgstr "ディレクトリ名(_D)" #: ../code/commands/browser-context-commands.cpp:408 msgid "_Document Name:" msgstr "ドキュメント名(_D)" #: ../code/commands/browser-context-commands.cpp:411 msgid "C_reate" msgstr "作成(_C)" #: ../code/commands/browser-context-commands.cpp:415 msgid "New Directory" msgstr "新規ディレクトリ" #: ../code/commands/browser-context-commands.cpp:416 #: ../code/commands/file-tasks/task-new.cpp:36 msgid "New Document" msgstr "新規ドキュメント" #: ../code/commands/browser-context-commands.cpp:436 #: ../code/commands/file-tasks/task-open-file.cpp:22 msgid "Choose a text file to open" msgstr "開くテキストファイルの選択" #: ../code/commands/browser-context-commands.cpp:558 #: ../code/commands/browser-context-commands.cpp:569 msgid "Account successfully created" msgstr "" #: ../code/commands/browser-context-commands.cpp:562 msgid "When re-connecting to the server, the new account will be used." msgstr "" #: ../code/commands/browser-context-commands.cpp:573 msgid "" "The certificate has been stored at %1.\n" "\n" "To login to this account, set the certificate in Gobby's preferences and re-" "connect to the server." msgstr "" #: ../code/commands/browser-context-commands.cpp:586 #, fuzzy msgid "Failed to create account" msgstr "ドキュメント\"%1\"の作成に失敗しました" #: ../code/commands/browser-context-commands.cpp:589 msgid "Could not save the certificate for the account: %1" msgstr "" #: ../code/commands/file-tasks/task-export-html.cpp:27 msgid "Choose a location to export document \"%1\" to" msgstr "\"%1\"をエクスポートする先の選択" #: ../code/commands/file-tasks/task-open.cpp:46 msgid "Querying \"%1\"..." msgstr "\"%1\"の問い合わせ..." #: ../code/commands/file-tasks/task-open.cpp:103 #: ../code/operations/operation-open.cpp:474 #: ../code/operations/operation-open-multiple.cpp:180 msgid "Failed to open document \"%1\"" msgstr "\"%1\"を開くのに失敗しました" #: ../code/commands/file-tasks/task-save.cpp:24 msgid "Choose a location to save document \"%1\" to" msgstr "\"%1\"を保存する先の選択" #. Help browser could not be invoked, show an error message to the user. #: ../code/commands/help-commands.cpp:70 msgid "There was an error displaying help." msgstr "ヘルプ表示時にエラーが発生しました。" #: ../code/commands/help-commands.cpp:93 msgid "Contributors:" msgstr "貢献者:" #: ../code/commands/help-commands.cpp:99 msgid "British English:" msgstr "British English:" #: ../code/commands/help-commands.cpp:101 msgid "German:" msgstr "German:" #: ../code/commands/help-commands.cpp:118 msgid "" "Permission to use, copy, modify, and/or distribute this software for any " "urpose with or without fee is hereby granted, provided that the above " "copyright notice and this permission notice appear in all copies.\n" "\n" "THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES " "WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF " "MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY " "SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES " "WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION " "OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN " "CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE." msgstr "" #: ../code/commands/subscription-commands.cpp:148 msgid "" "The connection to the publisher of this document has been lost. Further " "changes to the document could not be synchronized to others anymore, " "therefore the document cannot be edited anymore.\n" "\n" "Please note also that it is possible that not all of your latest changes " "have reached the publisher before the connection was lost." msgstr "" "このドキュメントの発行者への接続が失われました。ドキュメントへの変更の同期が" "これ以上できないため、このドキュメントはこれ以上編集できません。\n" "\n" "発行者への接続が失われる前に伝達された変更でも適用されていない可能性がありま" "す。" #: ../code/commands/synchronization-commands.cpp:30 msgid "Synchronization in progress... %1%%" msgstr "同期中です... %1%%" #. Document cannot be used if an error happened #. during synchronization. #: ../code/commands/synchronization-commands.cpp:42 msgid "This document cannot be used." msgstr "このドキュメントは使用できません。" #: ../code/commands/synchronization-commands.cpp:45 #: ../code/commands/user-join-commands.cpp:43 msgid "" "If you have an idea what could have caused the problem, then you may attempt " "to solve it and try again (after having closed this document). Otherwise it " "is most likely a bug in the software. In that case, please file a bug report " "at http://gobby.0x539.de/trac/newticket and provide as much information as " "you can, including what you did when the problem occurred and how to " "reproduce the problem (if possible) so that we can fix the problem in a " "later version. Thank you." msgstr "" "もしなにがこの問題を引き起こしているか分かる場合、それを解決してもう一度試し" "てください(このドキュメントを閉じた後)。そうでなければこのソフトウェアのバグ" "の可能性があります。この場合は私たちは今後のバージョンで問題を修復できるよう" "に、http://gobby.0x539.de/trac/newticket(英語)にバグレポートを記録し、この問" "題が起きたときどんな操作をしていたか、または問題の再現の方法(可能ならば)を含" "むできるだけたくさんの情報を提供してください。よろしくお願いします。" #: ../code/commands/synchronization-commands.cpp:59 msgid "Synchronization failed: %1" msgstr "同期に失敗しました: %1" #. TODO: Adjust this for chat sessions #: ../code/commands/user-join-commands.cpp:38 msgid "" "You can still watch others editing the document, but you cannot edit it " "yourself." msgstr "" "このドキュメントが他人によって編集されるのを見ることができますが、編集はでき" "ません。" #: ../code/commands/user-join-commands.cpp:57 msgid "User Join failed: %1" msgstr "ユーザーの参加に失敗: %1" #: ../code/commands/user-join-commands.cpp:64 msgid "Permissions are not granted to modify the document." msgstr "" #: ../code/core/browser.cpp:111 msgid "_Direct Connection" msgstr "直接接続(_D)" #: ../code/core/browser.cpp:113 msgid "Host Name:" msgstr "ホスト名:" #: ../code/core/certificatemanager.cpp:83 #, c-format msgid "Failed to write Diffie-Hellman parameters to \"%s\": %s" msgstr "" #: ../code/core/certificatemanager.cpp:276 #, c-format msgid "Failed to read Diffie-Hellman parameters: %s" msgstr "" #: ../code/core/certificatemanager.cpp:338 msgid "File does not contain a X.509 certificate" msgstr "" #: ../code/core/certificatemanager.cpp:414 msgid "Certificate does not belong to the chosen key" msgstr "" #: ../code/core/foldermanager.cpp:322 msgid "The document has been removed from the server." msgstr "" #: ../code/core/header.cpp:203 msgid "_None" msgstr "_None" #: ../code/core/header.cpp:227 msgid "_File" msgstr "ファイル(_F)" #: ../code/core/header.cpp:230 msgid "_Open..." msgstr "開く(_O)" #: ../code/core/header.cpp:233 msgid "Open _Location..." msgstr "場所を開く(_L)" #: ../code/core/header.cpp:237 msgid "Save _As..." msgstr "名前をつけて保存(_A)" #: ../code/core/header.cpp:241 ../code/core/iconmanager.cpp:43 msgid "Save All" msgstr "すべて保存" #: ../code/core/header.cpp:241 msgid "Save all open files locally" msgstr "開かれているすべてのファイルをローカルへ保存" #: ../code/core/header.cpp:243 msgid "Export As _HTML..." msgstr "HTMLとしてエクスポート(_H)" #: ../code/core/header.cpp:246 msgid "Connect _to Server..." msgstr "サーバーへ接続(_T)" #: ../code/core/header.cpp:251 msgid "_Edit" msgstr "編集(_E)" #: ../code/core/header.cpp:259 msgid "_Find..." msgstr "検索(_F)" #: ../code/core/header.cpp:261 msgid "Find Ne_xt" msgstr "次を検索(_X)" #: ../code/core/header.cpp:262 msgid "Find next match of phrase searched for" msgstr "フレーズ検索の次を検索" #: ../code/core/header.cpp:265 msgid "Find Pre_vious" msgstr "前を検索(_V)" #: ../code/core/header.cpp:266 msgid "Find previous match of phrase searched for" msgstr "フレーズ検索の前を検索" #: ../code/core/header.cpp:271 msgid "Find and Rep_lace..." msgstr "置換(_L)" #: ../code/core/header.cpp:275 msgid "Go to _Line..." msgstr "指定行へ移動(_L)" #: ../code/core/header.cpp:279 msgid "Pr_eferences..." msgstr "設定(_E)" #: ../code/core/header.cpp:281 msgid "_View" msgstr "表示(_V)" #: ../code/core/header.cpp:284 msgid "Reset User Colors" msgstr "ユーザーカラーをリセット" #: ../code/core/header.cpp:285 msgid "Hide user colors in current document" msgstr "このドキュメントユーザーカラーを非表示" #: ../code/core/header.cpp:289 msgid "Fullscreen" msgstr "" #: ../code/core/header.cpp:290 msgid "Show the editing window in fullscreen mode" msgstr "" #: ../code/core/header.cpp:294 msgid "Zoom In" msgstr "" #: ../code/core/header.cpp:297 msgid "Zoom Out" msgstr "" #: ../code/core/header.cpp:300 msgid "View Toolbar" msgstr "ツールバー" #: ../code/core/header.cpp:301 msgid "Whether to show the toolbar" msgstr "ツールバーを表示するかどうか" #: ../code/core/header.cpp:305 msgid "View Statusbar" msgstr "ステータスバー" #: ../code/core/header.cpp:306 msgid "Whether to show the statusbar" msgstr "ステータスバーを表示するかどうか" #: ../code/core/header.cpp:310 msgid "View Document Browser" msgstr "ドキュメントブラウザ" #: ../code/core/header.cpp:311 msgid "Whether to show the document browser" msgstr "ドキュメントブラウザを表示するかどうか" #: ../code/core/header.cpp:315 msgid "View Chat" msgstr "チャット" #: ../code/core/header.cpp:316 msgid "Whether to show the chat pane" msgstr "チャットを表示するかどうか" #: ../code/core/header.cpp:320 msgid "View Document User List" msgstr "ドキュメントユーザーリスト" #: ../code/core/header.cpp:321 msgid "Whether to show the user list for documents" msgstr "このドキュメントのユーザーリストを表示するかどうか" #: ../code/core/header.cpp:325 msgid "View Chat User List" msgstr "チャットユーザーリスト" #: ../code/core/header.cpp:326 msgid "Whether to show the user list for the chat" msgstr "チャットユーザーリストを表示するかどうか" #: ../code/core/header.cpp:330 msgid "_Highlight Mode" msgstr "強調表示モード(_H)" #: ../code/core/header.cpp:336 msgid "_Help" msgstr "ヘルプ(_H)" #: ../code/core/header.cpp:339 msgid "_Contents" msgstr "目次(_C)" #: ../code/core/header.cpp:340 msgid "Opens the Gobby manual" msgstr "Gobbyのマニュアルを開く" #: ../code/core/header.cpp:343 msgid "_About..." msgstr "Gobbyについて(_A)" #: ../code/core/header.cpp:344 msgid "Shows Gobby's copyright and credits" msgstr "Gobbyの著作権やクレジットの表示" #: ../code/core/iconmanager.cpp:49 msgid "User list" msgstr "ユーザーリスト" #: ../code/core/iconmanager.cpp:55 msgid "Document list" msgstr "ドキュメントリスト" #: ../code/core/iconmanager.cpp:61 ../code/window.cpp:52 msgid "Chat" msgstr "チャット" #: ../code/core/iconmanager.cpp:68 msgid "User Color Indicator" msgstr "ユーザーカラー表示" #: ../code/core/selfhoster.cpp:103 msgid "Generating 2048-bit Diffie-Hellman parameters..." msgstr "" #: ../code/core/selfhoster.cpp:135 #, fuzzy msgid "Failed to generate Diffie-Hellman parameters" msgstr "ノード\"%1\"の削除に失敗" #: ../code/core/selfhoster.cpp:137 msgid "" "This means that Perfect Forward Secrecy (PFS) is not available. Restart " "Gobby to re-try generating the parameters. The specific error was:\n" "\n" "%1" msgstr "" #: ../code/core/selfhoster.cpp:262 msgid "" "In order to start sharing your documents, choose a private key and " "certificate or create a new pair in the preferences" msgstr "" #: ../code/core/selfhoster.cpp:282 #, fuzzy msgid "Failed to share documents" msgstr "ドキュメント\"%1\"の保存に失敗しました" #: ../code/core/sessionuserview.cpp:39 msgid "User List" msgstr "ユーザーリスト" #: ../code/core/statusbar.cpp:418 msgid "Ln %1, Col %2\t%3" msgstr "%1行, %2列\t%3" #: ../code/core/statusbar.cpp:421 msgid "OVR" msgstr "上書" #: ../code/core/statusbar.cpp:421 msgid "INS" msgstr "挿入" #: ../code/core/textsessionview.cpp:638 msgid "Text written by %1" msgstr "%1によって書かれたテキスト" #: ../code/core/textsessionview.cpp:644 msgid "Unowned text" msgstr "所有していないテキスト" #: ../code/core/userlist.cpp:108 msgid "Users" msgstr "ユーザー" #: ../code/dialogs/connection-dialog.cpp:26 msgid "Connect to Server" msgstr "サーバーへ接続" #: ../code/dialogs/connection-dialog.cpp:30 msgid "Please enter a host name with which to establish a connection." msgstr "接続先のホスト名を入力してください" #: ../code/dialogs/connection-dialog.cpp:32 msgid "_Remote Endpoint:" msgstr "接続先(_R)" #: ../code/dialogs/document-location-dialog.cpp:28 msgid "Document Name:" msgstr "ドキュメント名:" #: ../code/dialogs/document-location-dialog.cpp:155 msgid "Select document's target location" msgstr "ドキュメントの対象場所を選択" #: ../code/dialogs/document-location-dialog.cpp:157 msgid "Choose a directory to create the document into:" msgstr "ドキュメントをいれるディレクトリ名:" #: ../code/dialogs/document-location-dialog.cpp:163 msgid "Select documents' target location" msgstr "ドキュメントの対象場所を選択" #: ../code/dialogs/document-location-dialog.cpp:165 msgid "Choose a directory to create the documents into:" msgstr "ドキュメントをいれるディレクトリ名:" #: ../code/dialogs/find-dialog.cpp:44 ../code/dialogs/find-dialog.cpp:142 msgid "Find" msgstr "検索" #: ../code/dialogs/find-dialog.cpp:47 msgid "_Search for:" msgstr "検索文字列(_S):" #: ../code/dialogs/find-dialog.cpp:49 msgid "Replace _with:" msgstr "置換先(_W)" #: ../code/dialogs/find-dialog.cpp:51 msgid "_Match case" msgstr "大文字小文字の区別(_M)" #: ../code/dialogs/find-dialog.cpp:52 msgid "Match _entire word only" msgstr "完全単語一致(_E)" #: ../code/dialogs/find-dialog.cpp:53 msgid "Search _backwards" msgstr "前へ検索(_B)" #: ../code/dialogs/find-dialog.cpp:54 msgid "Wra_p around" msgstr "行をはさむ(_P)" #: ../code/dialogs/find-dialog.cpp:100 msgid "Replace _All" msgstr "すべて置換(_A)" #: ../code/dialogs/find-dialog.cpp:102 msgid "_Replace" msgstr "置換(_R)" #: ../code/dialogs/find-dialog.cpp:142 msgid "Replace" msgstr "置換" #: ../code/dialogs/find-dialog.cpp:161 ../code/dialogs/find-dialog.cpp:177 msgid "Phrase \"%1\" has not been found" msgstr "フレーズ\"%1\"は発見されません" #: ../code/dialogs/find-dialog.cpp:335 msgid "No occurrence has been replaced" msgstr "置換されませんでした" #: ../code/dialogs/find-dialog.cpp:341 msgid "%1 occurrence has been replaced" msgid_plural "%1 occurrences have been replaced" msgstr[0] "%1件置換されました" #: ../code/dialogs/goto-dialog.cpp:26 msgid "Go to line" msgstr "指定行へ移動" #: ../code/dialogs/goto-dialog.cpp:29 msgid "Line _number:" msgstr "行番号(_N):" #: ../code/dialogs/goto-dialog.cpp:50 msgid "Go To _Line" msgstr "指定行へ移動(_L)" #: ../code/dialogs/initial-dialog.cpp:76 msgid "Generating 2048-bit RSA private key..." msgstr "" #: ../code/dialogs/initial-dialog.cpp:99 #, fuzzy msgid "Failed to generate private key" msgstr "ノード\"%1\"の削除に失敗" #: ../code/dialogs/initial-dialog.cpp:102 msgid "" "%1\n" "\n" "You can try again to create a key in the Security tab of the preferences " "dialog." msgstr "" #: ../code/dialogs/initial-dialog.cpp:141 msgid "Failed to generate self-signed certificate" msgstr "" #: ../code/dialogs/initial-dialog.cpp:144 msgid "" "%1\n" "\n" "You can try again to create a certificate in the Security tab of the " "preferences dialog." msgstr "" #: ../code/dialogs/initial-dialog.cpp:198 msgid "Choose a user color" msgstr "ユーザーカラーの選択" #: ../code/dialogs/initial-dialog.cpp:204 msgid "Welcome to Gobby" msgstr "Gobbyへようそこ" #: ../code/dialogs/initial-dialog.cpp:216 msgid "" "Before we start, a few options need to be configured. You can later change " "them by choosing Edit ▸ Preferences from the menu." msgstr "" "開始する前に、いくつかのオプションを設定する必要があります。これらは後から" "「設定」メニューで変更できます。" #: ../code/dialogs/initial-dialog.cpp:224 msgid "User Name" msgstr "ユーザー名" #: ../code/dialogs/initial-dialog.cpp:226 msgid "Your name as shown to other users." msgstr "他のユーザーへ表示されるあなたの名前" #: ../code/dialogs/initial-dialog.cpp:235 msgid "User Color" msgstr "ユーザーカラー" #: ../code/dialogs/initial-dialog.cpp:237 msgid "The color with which text you have written is branded." msgstr "あなたが書いた文章につけられる色" #: ../code/dialogs/initial-dialog.cpp:256 #, fuzzy msgid "Remote Connections" msgstr "保護された接続" #: ../code/dialogs/initial-dialog.cpp:262 #: ../code/dialogs/preferences-dialog.cpp:217 msgid "Allow remote users to edit local documents" msgstr "" #: ../code/dialogs/initial-dialog.cpp:268 msgid "Ask remote users for a password" msgstr "" #: ../code/dialogs/initial-dialog.cpp:273 #: ../code/dialogs/preferences-dialog.cpp:220 #, fuzzy msgid "Password:" msgstr "サーバーパスワード(_P):" #: ../code/dialogs/initial-dialog.cpp:297 msgid "No authentication (Not recommended)" msgstr "" #: ../code/dialogs/initial-dialog.cpp:304 msgid "" "Don't authenticate ourselves to remote users. Data transfer will be " "unencrypted." msgstr "" #: ../code/dialogs/initial-dialog.cpp:313 msgid "Create a self-signed certificate (Recommended)" msgstr "" #: ../code/dialogs/initial-dialog.cpp:321 msgid "" "It may take a minute or two until remote users can connect while the " "security certificate is being generated." msgstr "" #: ../code/dialogs/initial-dialog.cpp:331 msgid "Use an existing certificate (Expert option)" msgstr "" #: ../code/dialogs/initial-dialog.cpp:338 msgid "" "Use an existing private key and certificate for authentication. The files " "must be in PEM format." msgstr "" #: ../code/dialogs/initial-dialog.cpp:347 #: ../code/dialogs/preferences-dialog.cpp:766 msgid "Private key:" msgstr "" #: ../code/dialogs/initial-dialog.cpp:349 #: ../code/dialogs/preferences-dialog.cpp:772 msgid "Certificate:" msgstr "" #: ../code/dialogs/open-location-dialog.cpp:26 msgid "Open Location" msgstr "場所を開く" #: ../code/dialogs/open-location-dialog.cpp:27 msgid "Enter the _location (URI) of the file you would like to open:" msgstr "開きたいファイルがある場所(URI)(_L):" #: ../code/dialogs/password-dialog.cpp:25 msgid "Password Required" msgstr "パスワードが必要です" #: ../code/dialogs/password-dialog.cpp:31 msgid "Connection to host \"%1\" requires a password." msgstr "ホスト\"%1\"への接続にはパスワードが必要です。" #: ../code/dialogs/password-dialog.cpp:32 msgid "Invalid password for host \"%1\". Please try again." msgstr "ホスト\"%1\"のパスワードが違います。再試行してください。" #: ../code/dialogs/password-dialog.cpp:35 msgid "Server _Password:" msgstr "サーバーパスワード(_P):" #: ../code/dialogs/preferences-dialog.cpp:198 msgid "Settings" msgstr "設定" #: ../code/dialogs/preferences-dialog.cpp:199 #, fuzzy msgid "Remote Users" msgstr "ユーザーカラーをリセット" #: ../code/dialogs/preferences-dialog.cpp:200 #, fuzzy msgid "Local Documents" msgstr "新規ドキュメント" #: ../code/dialogs/preferences-dialog.cpp:202 msgid "User name:" msgstr "ユーザー名:" #: ../code/dialogs/preferences-dialog.cpp:204 msgid "User color:" msgstr "ユーザーカラー:" #: ../code/dialogs/preferences-dialog.cpp:206 msgid "Color intensity:" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:208 msgid "Choose a new user color" msgstr "新しいユーザーカラーを選択してください" #: ../code/dialogs/preferences-dialog.cpp:209 msgid "Show cursors of remote users" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:210 msgid "Show selections of remote users" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:212 #, fuzzy msgid "Highlight current line of remote users" msgstr "現在の行をハイライトする" #: ../code/dialogs/preferences-dialog.cpp:214 msgid "Indicate cursor position of remote users in the scrollbar" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:219 msgid "Require remote users to enter a password" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:222 msgid "Port:" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:226 msgid "Remember local documents after Gobby restart" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:227 #, fuzzy msgid "Local documents directory:" msgstr "ホストディレクトリ:" #: ../code/dialogs/preferences-dialog.cpp:423 msgid "Tab Stops" msgstr "タブストップ" #: ../code/dialogs/preferences-dialog.cpp:424 msgid "Indentation" msgstr "インデント" #: ../code/dialogs/preferences-dialog.cpp:425 msgid "Home/End Behavior" msgstr "Home/Endキーの挙動" #: ../code/dialogs/preferences-dialog.cpp:426 msgid "File Saving" msgstr "ファイルの保存" #: ../code/dialogs/preferences-dialog.cpp:427 msgid "_Tab width:" msgstr "タブ幅(_T):" #: ../code/dialogs/preferences-dialog.cpp:429 msgid "Insert _spaces instead of tabs" msgstr "タブ文字の代わりにスペースを挿入する(_I)" #: ../code/dialogs/preferences-dialog.cpp:430 msgid "Enable automatic _indentation" msgstr "オートインデントを有効にする(_I)" #: ../code/dialogs/preferences-dialog.cpp:431 msgid "Smart _home/end" msgstr "スマートhome/end(_H)" #: ../code/dialogs/preferences-dialog.cpp:432 msgid "Enable _automatic saving of documents" msgstr "ドキュメントの自動保存を有効にする(_A)" #: ../code/dialogs/preferences-dialog.cpp:434 msgid "Autosave interval in _minutes:" msgstr "自動保存間隔(分)(_M):" #: ../code/dialogs/preferences-dialog.cpp:455 msgid "" "With this option enabled, Home/End keys move to first/last character before " "going to the start/end of the line." msgstr "" "このオプションによって、Home/Endキーは行頭/行末にいく前に最初/最後の文字へ移" "動するようになります。" #: ../code/dialogs/preferences-dialog.cpp:525 msgid "Text Wrapping" msgstr "ワードラップ" #: ../code/dialogs/preferences-dialog.cpp:526 msgid "Line Numbers" msgstr "行番号" #: ../code/dialogs/preferences-dialog.cpp:527 msgid "Current Line" msgstr "現在行" #: ../code/dialogs/preferences-dialog.cpp:528 msgid "Right Margin" msgstr "右マージン" #: ../code/dialogs/preferences-dialog.cpp:529 msgid "Bracket Matching" msgstr "対応する括弧の表示" #: ../code/dialogs/preferences-dialog.cpp:530 msgid "Whitespace Display" msgstr "空白文字の表示" #: ../code/dialogs/preferences-dialog.cpp:531 msgid "Enable text wrapping" msgstr "ワードラップを有効にする" #: ../code/dialogs/preferences-dialog.cpp:532 msgid "Do not split words over two lines" msgstr "一単語を二行に分割しない" #: ../code/dialogs/preferences-dialog.cpp:533 msgid "Display line numbers" msgstr "行番号を表示する" #: ../code/dialogs/preferences-dialog.cpp:534 msgid "Highlight current line" msgstr "現在の行をハイライトする" #: ../code/dialogs/preferences-dialog.cpp:535 msgid "Display right margin" msgstr "右マージンの表示" #: ../code/dialogs/preferences-dialog.cpp:536 msgid "Right margin at column:" msgstr "右マージンの列数:" #: ../code/dialogs/preferences-dialog.cpp:537 msgid "Highlight matching bracket" msgstr "対応する括弧のハイライト" #: ../code/dialogs/preferences-dialog.cpp:589 msgid "Display no whitespace" msgstr "空白文字を表示しない" #: ../code/dialogs/preferences-dialog.cpp:592 msgid "Display spaces" msgstr "スペースの表示" #: ../code/dialogs/preferences-dialog.cpp:596 msgid "Display tabs" msgstr "タブ文字の表示" #: ../code/dialogs/preferences-dialog.cpp:600 msgid "Display tabs and spaces" msgstr "タブ文字とスペースの表示" #: ../code/dialogs/preferences-dialog.cpp:651 msgid "Toolbar" msgstr "ツールバー" #: ../code/dialogs/preferences-dialog.cpp:652 msgid "Font" msgstr "フォント" #: ../code/dialogs/preferences-dialog.cpp:653 msgid "Color Scheme" msgstr "カラースキーム" #: ../code/dialogs/preferences-dialog.cpp:661 msgid "Show text only" msgstr "テキストのみ表示" #: ../code/dialogs/preferences-dialog.cpp:663 msgid "Show icons only" msgstr "アイコンのみ表示" #: ../code/dialogs/preferences-dialog.cpp:665 msgid "Show both icons and text" msgstr "アイコンとテキストの表示" #: ../code/dialogs/preferences-dialog.cpp:667 msgid "Show text besides icons" msgstr "アイコンの隣にテキストを表示" #: ../code/dialogs/preferences-dialog.cpp:756 msgid "Trusted CAs" msgstr "信頼したCA" #: ../code/dialogs/preferences-dialog.cpp:757 msgid "Secure Connection" msgstr "保護された接続" #: ../code/dialogs/preferences-dialog.cpp:758 #, fuzzy msgid "Authentication" msgstr "インデント" #: ../code/dialogs/preferences-dialog.cpp:759 msgid "Select a file containing trusted CAs" msgstr "信頼されたCAを含むファイルの選択" #: ../code/dialogs/preferences-dialog.cpp:764 #, fuzzy msgid "None" msgstr "_None" #: ../code/dialogs/preferences-dialog.cpp:765 msgid "Use a certificate" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:767 msgid "Select a private key file" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:769 #: ../code/dialogs/preferences-dialog.cpp:776 #, fuzzy msgid "Create New..." msgstr "ドキュメントの作成(_C)" #: ../code/dialogs/preferences-dialog.cpp:773 msgid "Select a certificate file" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:801 msgid "Use TLS if possible" msgstr "可能であればTLSを使用する" #: ../code/dialogs/preferences-dialog.cpp:804 msgid "Always use TLS" msgstr "常にTLSを使用する" #: ../code/dialogs/preferences-dialog.cpp:892 msgid "Error reading file:" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:916 msgid "2048-bit RSA private key is being generated, please wait..." msgstr "" #: ../code/dialogs/preferences-dialog.cpp:948 msgid "Select a location for the generated key" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:966 msgid "Select a location for the generated certificate" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:1068 msgid "Preferences" msgstr "設定" #: ../code/dialogs/preferences-dialog.cpp:1073 msgid "User" msgstr "ユーザー" #: ../code/dialogs/preferences-dialog.cpp:1074 msgid "Editor" msgstr "エディタ" #: ../code/dialogs/preferences-dialog.cpp:1075 msgid "View" msgstr "表示" #: ../code/dialogs/preferences-dialog.cpp:1076 msgid "Appearance" msgstr "外観" #: ../code/dialogs/preferences-dialog.cpp:1077 msgid "Security" msgstr "セキュリティ" #: ../code/main.cpp:194 msgid "Gobby options" msgstr "Gobbyオプション" #: ../code/main.cpp:194 msgid "Options related to Gobby" msgstr "Gobbyに関連したオプション" #: ../code/main.cpp:199 msgid "Display version information and exit" msgstr "バージョン情報を表示して終了" #: ../code/main.cpp:206 msgid "Also start a new Gobby instance when there is one running already" msgstr "Gobbyが既にひとつ起動していても新しいインスタンスを起動する" #: ../code/main.cpp:214 msgid "Connect to given host on startup, can be given multiple times" msgstr "起動時に与えられたホストへ接続します。複数回指定できます。" #: ../code/main.cpp:215 msgid "HOSTNAME" msgstr "HOSTNAME" #: ../code/operations/operation-delete.cpp:65 msgid "Removing node \"%1\"..." msgstr "ノード\"%1\"を削除..." #: ../code/operations/operation-delete.cpp:75 msgid "Failed to delete node \"%1\"" msgstr "ノード\"%1\"の削除に失敗" #: ../code/operations/operation-export-html.cpp:168 #, c-format msgid "written by: %s" msgstr "%sによって書かれた" #: ../code/operations/operation-export-html.cpp:241 msgid "" msgstr "<日付を表示できません>" #. %1$s is session name/hostname #. %2$s is path within the session #. %3$s is current date as formatted by %c, #. %4$s is a link to the gobby site, it must be present because #. we need to handle that manually to insert a hyperlink #. instead of just printf'ing it. #: ../code/operations/operation-export-html.cpp:254 #, c-format msgid "Document generated from %1$s:%2$s at %3$s by %4$s" msgstr "ドキュメントは%3$sの%1$s:%2$sから%4$sによって生成された" #: ../code/operations/operation-export-html.cpp:395 msgid "Participants" msgstr "参加者" #: ../code/operations/operation-export-html.cpp:475 msgid "Exporting document \"%1\" to \"%2\" in HTML..." msgstr "\"%1\"を\"%2\"へHTMLでエクスポート..." #: ../code/operations/operation-export-html.cpp:536 msgid "Failed to export document \"%1\" to HTML" msgstr "\"%1\"のHTMLでのエクスポートに失敗しました" #: ../code/operations/operation-new.cpp:75 msgid "Creating directory \"%1\"..." msgstr "ディレクトリ\"%1\"の作成..." #: ../code/operations/operation-new.cpp:76 msgid "Creating document \"%1\"..." msgstr "ドキュメント\"%1\"の作成" #: ../code/operations/operation-new.cpp:88 msgid "Failed to create directory \"%1\"" msgstr "ディレクトリ\"%1\"の作成に失敗しました" #: ../code/operations/operation-new.cpp:89 msgid "Failed to create document \"%1\"" msgstr "ドキュメント\"%1\"の作成に失敗しました" #. Translators: This is the 8 bit encoding that is tried when #. autodetecting a file's encoding. #: ../code/operations/operation-open.cpp:38 msgid "ISO-8859-1" msgstr "ISO-8859-1" #: ../code/operations/operation-open.cpp:114 msgid "Opening document \"%1\"..." msgstr "ドキュメント\"%1\"を開く..." #: ../code/operations/operation-open.cpp:134 msgid "" "The directory into which the new document was supposed to be inserted has " "been removed" msgstr "新しいドキュメントを挿入しようとしたディレクトリは削除されています" #: ../code/operations/operation-open.cpp:323 msgid "The file contains data not in the specified encoding" msgstr "入力されたエンコーディングでないデータがファイルに含まれています" #: ../code/operations/operation-open.cpp:334 msgid "" "The file either contains data in an unknown encoding, or it contains binary " "data." msgstr "未知のエンコーディングか、バイナリデータがファイルに含まれています" #: ../code/operations/operation-open-multiple.cpp:91 msgid "Parent folder was removed" msgstr "親フォルダは削除されました" #: ../code/operations/operation-open-multiple.cpp:193 msgid "Failed to open multiple documents" msgstr "複数のファイルを開くのに失敗" #: ../code/operations/operation-save.cpp:97 msgid "Saving document \"%1\" to \"%2\"..." msgstr "\"%1\"を\"%2\"へ保存..." #: ../code/operations/operation-save.cpp:226 msgid "" "The document contains one or more characters that cannot be encoded in the " "specified character coding." msgstr "" "指定された文字エンコーディングでエンコードできない文字がドキュメントに含まれ" "ています" #: ../code/operations/operation-save.cpp:287 msgid "Failed to save document \"%1\"" msgstr "ドキュメント\"%1\"の保存に失敗しました" #: ../code/operations/operation-subscribe-path.cpp:33 msgid "Invalid path: \"%1\"" msgstr "" #: ../code/operations/operation-subscribe-path.cpp:45 #, fuzzy msgid "Invalid path component: \"%1\"" msgstr "\"%1\"を開くのに失敗しました" #: ../code/operations/operation-subscribe-path.cpp:135 #: ../code/operations/operation-subscribe-path.cpp:200 #, fuzzy msgid "Subscribing to \"%1\"..." msgstr "%1へ加入..." #: ../code/operations/operation-subscribe-path.cpp:149 #: ../code/operations/operation-subscribe-path.cpp:212 #, fuzzy msgid "Failed to connect to \"%1\"" msgstr "ディレクトリ\"%1\"の作成に失敗しました" #: ../code/operations/operation-subscribe-path.cpp:164 msgid "URI scheme \"%1\" not supported" msgstr "" #: ../code/operations/operation-subscribe-path.cpp:193 #, fuzzy msgid "Connecting to \"%1\"..." msgstr "\"%1\"への接続が失敗しました" #: ../code/operations/operation-subscribe-path.cpp:220 #: ../code/operations/operation-subscribe-path.cpp:319 #: ../code/operations/operation-subscribe-path.cpp:356 #: ../code/operations/operation-subscribe-path.cpp:452 #: ../code/operations/operation-subscribe-path.cpp:472 #, fuzzy msgid "Could not subscribe to \"%1\"" msgstr "\"%1\"の解決に失敗しました" #: ../code/operations/operation-subscribe-path.cpp:322 #: ../code/operations/operation-subscribe-path.cpp:358 #, fuzzy msgid "Path \"%1\" does not exist" msgstr "デバイス\"%1\"は存在しません" #: ../code/util/file.cpp:65 ../code/util/file.cpp:74 msgid "Could not create directory \"%1\": %2" msgstr "ディレクトリ\"%1\"を作成できませんでした: %2" #: ../code/util/uri.cpp:91 msgid "Device \"%1\" does not exist" msgstr "デバイス\"%1\"は存在しません" #: ../code/window.cpp:104 msgid "This Computer" msgstr "" #: ../code/window.cpp:129 msgid "Document Browser" msgstr "ドキュメントブラウザ" #: ../code/window.cpp:182 msgid "You are still connected to a session" msgstr "まだセッションに接続されています" #: ../code/window.cpp:190 msgid "Do you want to close Gobby nevertheless?" msgstr "それでもGobbyを閉じてもいいですか?" #: ../code/window.cpp:196 msgid "C_ancel" msgstr "キャンセル(_C)" #: ../gobby-0.5.desktop.in.h:1 msgid "Gobby" msgstr "Gobby" #: ../gobby-0.5.desktop.in.h:2 msgid "Collaborative Editor" msgstr "共同作業テキストエディタ" #: ../gobby-0.5.desktop.in.h:3 msgid "Gobby Collaborative Editor (0.5)" msgstr "Gobby Collaborative Editor (0.5)" #: ../gobby-0.5.desktop.in.h:4 msgid "Edit text files collaboratively" msgstr "テキストを共同作業で編集する" #~ msgid "" #~ "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.\n" #~ "\n" #~ "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.\n" #~ "\n" #~ "You should have received a copy of the GNU General Public License along " #~ "with this program; if not, write to the Free Software Foundation, Inc., " #~ "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA" #~ msgstr "" #~ "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.\n" #~ "\n" #~ "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.\n" #~ "\n" #~ "You should have received a copy of the GNU General Public License along " #~ "with this program; if not, write to the Free Software Foundation, Inc., " #~ "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA" #~ msgid "User Join in progress..." #~ msgstr "ユーザーの参加処理を行っています..." #, fuzzy #~ msgid "Failed to resolve \"%1\": %2" #~ msgstr "\"%1\"の解決に失敗しました" #~ msgid "Resolving \"%1\"..." #~ msgstr "\"%1\"を解決しています..." #~ msgid "Paths" #~ msgstr "パス" #~ msgid "" #~ "The directory into which locally hosted sessions are permanently stored" #~ msgstr "ローカルでホストされたセッションが恒久的に記憶されるディレクトリ" po/pt_BR.po000066400000000000000000001033771304552132100130510ustar00rootroot00000000000000# Brazilian translation of Gobby # Copyright © 2014 Free Software Foundation, Inc. # This file is distributed under the same license as the gobby package. # Vitor Lobo , 2014. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: gobby 0.4.94\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2014-08-23 13:43-0400\n" "PO-Revision-Date: 2014-07-11 18:12+0100\n" "Last-Translator: Vitor Lobo \n" "Language-Team: Portuguese Brazilian \n" "Language: pt_BR\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n!=1;\n" #: ../code/commands/auth-commands.cpp:196 #, fuzzy msgid "Password required" msgstr "Senha Requerida" #: ../code/commands/auth-commands.cpp:214 msgid "Incorrect password" msgstr "" #: ../code/commands/browser-commands.cpp:288 msgid "Subscribing to %1..." msgstr "" #: ../code/commands/browser-commands.cpp:301 msgid "Subscribing to chat on %1..." msgstr "" #: ../code/commands/browser-commands.cpp:591 msgid "Subscription failed" msgstr "Assinatura falhou" #: ../code/commands/browser-context-commands.cpp:86 msgid "Could not find a location where to store the certificate" msgstr "" #: ../code/commands/browser-context-commands.cpp:153 msgid "_Remove" msgstr "" #: ../code/commands/browser-context-commands.cpp:188 #, fuzzy msgid "_Disconnect from Server" msgstr "Conectar ao Servidor" #: ../code/commands/browser-context-commands.cpp:220 #, fuzzy msgid "Create _Account..." msgstr "Criar Diretório..." #: ../code/commands/browser-context-commands.cpp:245 msgid "Create Do_cument..." msgstr "" #: ../code/commands/browser-context-commands.cpp:280 #, fuzzy msgid "Create Di_rectory..." msgstr "Criar Diretório..." #: ../code/commands/browser-context-commands.cpp:293 msgid "_Open Document..." msgstr "_Abrir Documento..." #: ../code/commands/browser-context-commands.cpp:312 msgid "_Permissions..." msgstr "" #: ../code/commands/browser-context-commands.cpp:324 msgid "D_elete" msgstr "" #: ../code/commands/browser-context-commands.cpp:405 msgid "Choose a name for the directory" msgstr "Escolha um nome para o diretório" #: ../code/commands/browser-context-commands.cpp:406 msgid "Choose a name for the document" msgstr "Escolha um nome para o documento" #: ../code/commands/browser-context-commands.cpp:407 msgid "_Directory Name:" msgstr "_Nome do diretório" #: ../code/commands/browser-context-commands.cpp:408 msgid "_Document Name:" msgstr "_Nome do documento" #: ../code/commands/browser-context-commands.cpp:411 msgid "C_reate" msgstr "C_riar" #: ../code/commands/browser-context-commands.cpp:415 msgid "New Directory" msgstr "Novo Diretório" #: ../code/commands/browser-context-commands.cpp:416 #: ../code/commands/file-tasks/task-new.cpp:36 msgid "New Document" msgstr "Novo Documento" #: ../code/commands/browser-context-commands.cpp:436 #: ../code/commands/file-tasks/task-open-file.cpp:22 msgid "Choose a text file to open" msgstr "Escolha um arquivo de texto para abrir" #: ../code/commands/browser-context-commands.cpp:558 #: ../code/commands/browser-context-commands.cpp:569 msgid "Account successfully created" msgstr "" #: ../code/commands/browser-context-commands.cpp:562 msgid "When re-connecting to the server, the new account will be used." msgstr "" #: ../code/commands/browser-context-commands.cpp:573 msgid "" "The certificate has been stored at %1.\n" "\n" "To login to this account, set the certificate in Gobby's preferences and re-" "connect to the server." msgstr "" #: ../code/commands/browser-context-commands.cpp:586 #, fuzzy msgid "Failed to create account" msgstr "Falha ao criar documento \"%1\"" #: ../code/commands/browser-context-commands.cpp:589 msgid "Could not save the certificate for the account: %1" msgstr "" #: ../code/commands/file-tasks/task-export-html.cpp:27 msgid "Choose a location to export document \"%1\" to" msgstr "Escolha um local para exportar o documento \"%1\" para" #: ../code/commands/file-tasks/task-open.cpp:46 msgid "Querying \"%1\"..." msgstr "Consultando \"%1\"..." #: ../code/commands/file-tasks/task-open.cpp:103 #: ../code/operations/operation-open.cpp:474 #: ../code/operations/operation-open-multiple.cpp:180 msgid "Failed to open document \"%1\"" msgstr "Falha ao abrir o documento \"%1\"" #: ../code/commands/file-tasks/task-save.cpp:24 msgid "Choose a location to save document \"%1\" to" msgstr "Escolha um local para salvar o documento \"%1\" para" #. Help browser could not be invoked, show an error message to the user. #: ../code/commands/help-commands.cpp:70 msgid "There was an error displaying help." msgstr "Erro ao exibir a ajuda" #: ../code/commands/help-commands.cpp:93 msgid "Contributors:" msgstr "Contribuintes:" #: ../code/commands/help-commands.cpp:99 msgid "British English:" msgstr "Inglês britânico:" #: ../code/commands/help-commands.cpp:101 msgid "German:" msgstr "Alemão:" #: ../code/commands/help-commands.cpp:118 msgid "" "Permission to use, copy, modify, and/or distribute this software for any " "urpose with or without fee is hereby granted, provided that the above " "copyright notice and this permission notice appear in all copies.\n" "\n" "THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES " "WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF " "MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY " "SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES " "WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION " "OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN " "CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE." msgstr "" #: ../code/commands/subscription-commands.cpp:148 msgid "" "The connection to the publisher of this document has been lost. Further " "changes to the document could not be synchronized to others anymore, " "therefore the document cannot be edited anymore.\n" "\n" "Please note also that it is possible that not all of your latest changes " "have reached the publisher before the connection was lost." msgstr "" #: ../code/commands/synchronization-commands.cpp:30 msgid "Synchronization in progress... %1%%" msgstr "Sincronização em andamento... %1%%" #. Document cannot be used if an error happened #. during synchronization. #: ../code/commands/synchronization-commands.cpp:42 msgid "This document cannot be used." msgstr "Este documento não pode ser usado" #: ../code/commands/synchronization-commands.cpp:45 #: ../code/commands/user-join-commands.cpp:43 msgid "" "If you have an idea what could have caused the problem, then you may attempt " "to solve it and try again (after having closed this document). Otherwise it " "is most likely a bug in the software. In that case, please file a bug report " "at http://gobby.0x539.de/trac/newticket and provide as much information as " "you can, including what you did when the problem occurred and how to " "reproduce the problem (if possible) so that we can fix the problem in a " "later version. Thank you." msgstr "" #: ../code/commands/synchronization-commands.cpp:59 msgid "Synchronization failed: %1" msgstr "Falha de sincronização: %1" #. TODO: Adjust this for chat sessions #: ../code/commands/user-join-commands.cpp:38 msgid "" "You can still watch others editing the document, but you cannot edit it " "yourself." msgstr "" "Você ainda pode ver outros documentos, mas você não pode editá-lovocê mesmo" #: ../code/commands/user-join-commands.cpp:57 msgid "User Join failed: %1" msgstr "" #: ../code/commands/user-join-commands.cpp:64 msgid "Permissions are not granted to modify the document." msgstr "" #: ../code/core/browser.cpp:111 msgid "_Direct Connection" msgstr "_Conexão direta" #: ../code/core/browser.cpp:113 msgid "Host Name:" msgstr "" #: ../code/core/certificatemanager.cpp:83 #, c-format msgid "Failed to write Diffie-Hellman parameters to \"%s\": %s" msgstr "" #: ../code/core/certificatemanager.cpp:276 #, c-format msgid "Failed to read Diffie-Hellman parameters: %s" msgstr "" #: ../code/core/certificatemanager.cpp:338 msgid "File does not contain a X.509 certificate" msgstr "" #: ../code/core/certificatemanager.cpp:414 msgid "Certificate does not belong to the chosen key" msgstr "" #: ../code/core/foldermanager.cpp:322 msgid "The document has been removed from the server." msgstr "" #: ../code/core/header.cpp:203 msgid "_None" msgstr "" #: ../code/core/header.cpp:227 msgid "_File" msgstr "_Arquivo" #: ../code/core/header.cpp:230 msgid "_Open..." msgstr "_Abrir..." #: ../code/core/header.cpp:233 msgid "Open _Location..." msgstr "Abrir _Local..." #: ../code/core/header.cpp:237 msgid "Save _As..." msgstr "Salvar _Como..." #: ../code/core/header.cpp:241 ../code/core/iconmanager.cpp:43 msgid "Save All" msgstr "Salvar Tudo" #: ../code/core/header.cpp:241 msgid "Save all open files locally" msgstr "Salvar todos os arquivos abertos localmente" #: ../code/core/header.cpp:243 msgid "Export As _HTML..." msgstr "Exportar Como _HTML..." #: ../code/core/header.cpp:246 msgid "Connect _to Server..." msgstr "Conectar _ao Servidor" #: ../code/core/header.cpp:251 msgid "_Edit" msgstr "_Editar" #: ../code/core/header.cpp:259 msgid "_Find..." msgstr "_Encontrar" #: ../code/core/header.cpp:261 msgid "Find Ne_xt" msgstr "Localizar Pr_óximo" #: ../code/core/header.cpp:262 msgid "Find next match of phrase searched for" msgstr "Encontrar a próxima parte da frase procurada" #: ../code/core/header.cpp:265 msgid "Find Pre_vious" msgstr "Localizar Ante_rior" #: ../code/core/header.cpp:266 msgid "Find previous match of phrase searched for" msgstr "Encontrar a parte anterior da frase procurada" #: ../code/core/header.cpp:271 msgid "Find and Rep_lace..." msgstr "Localizar e Subs_tituir" #: ../code/core/header.cpp:275 msgid "Go to _Line..." msgstr "Vá para a _Linha..." #: ../code/core/header.cpp:279 msgid "Pr_eferences..." msgstr "Pr_eferências" #: ../code/core/header.cpp:281 msgid "_View" msgstr "_Ver" #: ../code/core/header.cpp:284 msgid "Reset User Colors" msgstr "Redefinir as cores do usuário" #: ../code/core/header.cpp:285 msgid "Hide user colors in current document" msgstr "Esconder as cores do usuário no documento atual" #: ../code/core/header.cpp:289 msgid "Fullscreen" msgstr "" #: ../code/core/header.cpp:290 msgid "Show the editing window in fullscreen mode" msgstr "" #: ../code/core/header.cpp:294 msgid "Zoom In" msgstr "" #: ../code/core/header.cpp:297 msgid "Zoom Out" msgstr "" #: ../code/core/header.cpp:300 msgid "View Toolbar" msgstr "Ver a Barra de Ferramentas" #: ../code/core/header.cpp:301 msgid "Whether to show the toolbar" msgstr "Desejo mostrar a barra de ferramentas" #: ../code/core/header.cpp:305 msgid "View Statusbar" msgstr "Ver a Barra de Status" #: ../code/core/header.cpp:306 msgid "Whether to show the statusbar" msgstr "Desejo mostrar a Barra de Status" #: ../code/core/header.cpp:310 msgid "View Document Browser" msgstr "Exibir o Documento no Navegador" #: ../code/core/header.cpp:311 msgid "Whether to show the document browser" msgstr "Desejo mostrar o navegador do documento" #: ../code/core/header.cpp:315 msgid "View Chat" msgstr "Exibir Chat" #: ../code/core/header.cpp:316 msgid "Whether to show the chat pane" msgstr "Desejo mostrar o painel do Chat" #: ../code/core/header.cpp:320 msgid "View Document User List" msgstr "Ver documento lista de usuário" #: ../code/core/header.cpp:321 msgid "Whether to show the user list for documents" msgstr "Desejo mostrar a lista de usuários para documentos" #: ../code/core/header.cpp:325 msgid "View Chat User List" msgstr "Ver lista de usuários do Chat" #: ../code/core/header.cpp:326 msgid "Whether to show the user list for the chat" msgstr "Desejo mostrar a lista de usuário para o chat" #: ../code/core/header.cpp:330 msgid "_Highlight Mode" msgstr "" #: ../code/core/header.cpp:336 msgid "_Help" msgstr "_Ajuda" #: ../code/core/header.cpp:339 msgid "_Contents" msgstr "_Conteúdo" #: ../code/core/header.cpp:340 msgid "Opens the Gobby manual" msgstr "" #: ../code/core/header.cpp:343 msgid "_About..." msgstr "_Sobre" #: ../code/core/header.cpp:344 msgid "Shows Gobby's copyright and credits" msgstr "Mostrar direitos autoriais e créditos do Gobby" #: ../code/core/iconmanager.cpp:49 msgid "User list" msgstr "Lista de Usuários" #: ../code/core/iconmanager.cpp:55 msgid "Document list" msgstr "Lista de Documentos" #: ../code/core/iconmanager.cpp:61 ../code/window.cpp:52 msgid "Chat" msgstr "" #: ../code/core/iconmanager.cpp:68 msgid "User Color Indicator" msgstr "Indicador de cor do usuário" #: ../code/core/selfhoster.cpp:103 msgid "Generating 2048-bit Diffie-Hellman parameters..." msgstr "" #: ../code/core/selfhoster.cpp:135 #, fuzzy msgid "Failed to generate Diffie-Hellman parameters" msgstr "Falha ao excluir nó \"%1\"" #: ../code/core/selfhoster.cpp:137 msgid "" "This means that Perfect Forward Secrecy (PFS) is not available. Restart " "Gobby to re-try generating the parameters. The specific error was:\n" "\n" "%1" msgstr "" #: ../code/core/selfhoster.cpp:262 msgid "" "In order to start sharing your documents, choose a private key and " "certificate or create a new pair in the preferences" msgstr "" #: ../code/core/selfhoster.cpp:282 #, fuzzy msgid "Failed to share documents" msgstr "Falha ao salvar o documento \"%1\"" #: ../code/core/sessionuserview.cpp:39 msgid "User List" msgstr "Lista de Usuários" #: ../code/core/statusbar.cpp:418 msgid "Ln %1, Col %2\t%3" msgstr "" #: ../code/core/statusbar.cpp:421 msgid "OVR" msgstr "" #: ../code/core/statusbar.cpp:421 msgid "INS" msgstr "" #: ../code/core/textsessionview.cpp:638 msgid "Text written by %1" msgstr "" #: ../code/core/textsessionview.cpp:644 msgid "Unowned text" msgstr "Texto sem dono" #: ../code/core/userlist.cpp:108 msgid "Users" msgstr "Usuários" #: ../code/dialogs/connection-dialog.cpp:26 msgid "Connect to Server" msgstr "Conectar ao Servidor" #: ../code/dialogs/connection-dialog.cpp:30 msgid "Please enter a host name with which to establish a connection." msgstr "" "Por favor, digite um nome de host com o qual deseja estabelecer uma conexão." #: ../code/dialogs/connection-dialog.cpp:32 msgid "_Remote Endpoint:" msgstr "" #: ../code/dialogs/document-location-dialog.cpp:28 msgid "Document Name:" msgstr "Nome do Documento" #: ../code/dialogs/document-location-dialog.cpp:155 msgid "Select document's target location" msgstr "Selecione o local de destino do documento" #: ../code/dialogs/document-location-dialog.cpp:157 msgid "Choose a directory to create the document into:" msgstr "" #: ../code/dialogs/document-location-dialog.cpp:163 msgid "Select documents' target location" msgstr "Selecione o local de destino dos documentos" #: ../code/dialogs/document-location-dialog.cpp:165 msgid "Choose a directory to create the documents into:" msgstr "Escolha um diretório para criar os documentos em:" #: ../code/dialogs/find-dialog.cpp:44 ../code/dialogs/find-dialog.cpp:142 msgid "Find" msgstr "Localizar" #: ../code/dialogs/find-dialog.cpp:47 msgid "_Search for:" msgstr "_Pesquisar por:" #: ../code/dialogs/find-dialog.cpp:49 msgid "Replace _with:" msgstr "Substituir _com:" #: ../code/dialogs/find-dialog.cpp:51 msgid "_Match case" msgstr "" #: ../code/dialogs/find-dialog.cpp:52 msgid "Match _entire word only" msgstr "" #: ../code/dialogs/find-dialog.cpp:53 msgid "Search _backwards" msgstr "" #: ../code/dialogs/find-dialog.cpp:54 msgid "Wra_p around" msgstr "" #: ../code/dialogs/find-dialog.cpp:100 msgid "Replace _All" msgstr "Substituir _Tudo" #: ../code/dialogs/find-dialog.cpp:102 msgid "_Replace" msgstr "_Substituir" #: ../code/dialogs/find-dialog.cpp:142 msgid "Replace" msgstr "Substituir" #: ../code/dialogs/find-dialog.cpp:161 ../code/dialogs/find-dialog.cpp:177 msgid "Phrase \"%1\" has not been found" msgstr "" #: ../code/dialogs/find-dialog.cpp:335 msgid "No occurrence has been replaced" msgstr "Nenhuma ocorrência foi substituída" #: ../code/dialogs/find-dialog.cpp:341 msgid "%1 occurrence has been replaced" msgid_plural "%1 occurrences have been replaced" msgstr[0] "%1 ocorrência foi substituída" msgstr[1] "%1 as ocorrências foram substituídas" #: ../code/dialogs/goto-dialog.cpp:26 msgid "Go to line" msgstr "Ir até a linha" #: ../code/dialogs/goto-dialog.cpp:29 msgid "Line _number:" msgstr "Número de _linhas:" #: ../code/dialogs/goto-dialog.cpp:50 msgid "Go To _Line" msgstr "Ir até a _Linha" #: ../code/dialogs/initial-dialog.cpp:76 msgid "Generating 2048-bit RSA private key..." msgstr "" #: ../code/dialogs/initial-dialog.cpp:99 #, fuzzy msgid "Failed to generate private key" msgstr "Falha ao excluir nó \"%1\"" #: ../code/dialogs/initial-dialog.cpp:102 msgid "" "%1\n" "\n" "You can try again to create a key in the Security tab of the preferences " "dialog." msgstr "" #: ../code/dialogs/initial-dialog.cpp:141 msgid "Failed to generate self-signed certificate" msgstr "" #: ../code/dialogs/initial-dialog.cpp:144 msgid "" "%1\n" "\n" "You can try again to create a certificate in the Security tab of the " "preferences dialog." msgstr "" #: ../code/dialogs/initial-dialog.cpp:198 msgid "Choose a user color" msgstr "Escolha uma cor para o usuário" #: ../code/dialogs/initial-dialog.cpp:204 msgid "Welcome to Gobby" msgstr "Bem-vindo ao Gobby" #: ../code/dialogs/initial-dialog.cpp:216 msgid "" "Before we start, a few options need to be configured. You can later change " "them by choosing Edit ▸ Preferences from the menu." msgstr "" #: ../code/dialogs/initial-dialog.cpp:224 msgid "User Name" msgstr "Nome do Usuário" #: ../code/dialogs/initial-dialog.cpp:226 msgid "Your name as shown to other users." msgstr "Como seu nome será mostrado para outros usuários" #: ../code/dialogs/initial-dialog.cpp:235 msgid "User Color" msgstr "Cor do Usuário" #: ../code/dialogs/initial-dialog.cpp:237 msgid "The color with which text you have written is branded." msgstr "A cor com o texto que você escreveu é marcado" #: ../code/dialogs/initial-dialog.cpp:256 #, fuzzy msgid "Remote Connections" msgstr "Conexão Segura" #: ../code/dialogs/initial-dialog.cpp:262 #: ../code/dialogs/preferences-dialog.cpp:217 msgid "Allow remote users to edit local documents" msgstr "" #: ../code/dialogs/initial-dialog.cpp:268 msgid "Ask remote users for a password" msgstr "" #: ../code/dialogs/initial-dialog.cpp:273 #: ../code/dialogs/preferences-dialog.cpp:220 #, fuzzy msgid "Password:" msgstr "Senha Requerida" #: ../code/dialogs/initial-dialog.cpp:297 msgid "No authentication (Not recommended)" msgstr "" #: ../code/dialogs/initial-dialog.cpp:304 msgid "" "Don't authenticate ourselves to remote users. Data transfer will be " "unencrypted." msgstr "" #: ../code/dialogs/initial-dialog.cpp:313 msgid "Create a self-signed certificate (Recommended)" msgstr "" #: ../code/dialogs/initial-dialog.cpp:321 msgid "" "It may take a minute or two until remote users can connect while the " "security certificate is being generated." msgstr "" #: ../code/dialogs/initial-dialog.cpp:331 msgid "Use an existing certificate (Expert option)" msgstr "" #: ../code/dialogs/initial-dialog.cpp:338 msgid "" "Use an existing private key and certificate for authentication. The files " "must be in PEM format." msgstr "" #: ../code/dialogs/initial-dialog.cpp:347 #: ../code/dialogs/preferences-dialog.cpp:766 msgid "Private key:" msgstr "" #: ../code/dialogs/initial-dialog.cpp:349 #: ../code/dialogs/preferences-dialog.cpp:772 msgid "Certificate:" msgstr "" #: ../code/dialogs/open-location-dialog.cpp:26 msgid "Open Location" msgstr "Abrir Localização" #: ../code/dialogs/open-location-dialog.cpp:27 msgid "Enter the _location (URI) of the file you would like to open:" msgstr "" #: ../code/dialogs/password-dialog.cpp:25 msgid "Password Required" msgstr "Senha Requerida" #: ../code/dialogs/password-dialog.cpp:31 msgid "Connection to host \"%1\" requires a password." msgstr "" #: ../code/dialogs/password-dialog.cpp:32 msgid "Invalid password for host \"%1\". Please try again." msgstr "Senha inválida para o host \"%1\". Por favor, tente novamente." #: ../code/dialogs/password-dialog.cpp:35 msgid "Server _Password:" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:198 msgid "Settings" msgstr "Configurações" #: ../code/dialogs/preferences-dialog.cpp:199 #, fuzzy msgid "Remote Users" msgstr "Redefinir as cores do usuário" #: ../code/dialogs/preferences-dialog.cpp:200 #, fuzzy msgid "Local Documents" msgstr "Novo Documento" #: ../code/dialogs/preferences-dialog.cpp:202 msgid "User name:" msgstr "Nome do Usuário" #: ../code/dialogs/preferences-dialog.cpp:204 msgid "User color:" msgstr "Cor do usuário:" #: ../code/dialogs/preferences-dialog.cpp:206 msgid "Color intensity:" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:208 msgid "Choose a new user color" msgstr "Escolha uma nova cor para o usuário" #: ../code/dialogs/preferences-dialog.cpp:209 msgid "Show cursors of remote users" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:210 msgid "Show selections of remote users" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:212 #, fuzzy msgid "Highlight current line of remote users" msgstr "Realçar linha atual" #: ../code/dialogs/preferences-dialog.cpp:214 msgid "Indicate cursor position of remote users in the scrollbar" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:219 msgid "Require remote users to enter a password" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:222 msgid "Port:" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:226 msgid "Remember local documents after Gobby restart" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:227 #, fuzzy msgid "Local documents directory:" msgstr "Diretório do Host:" #: ../code/dialogs/preferences-dialog.cpp:423 msgid "Tab Stops" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:424 msgid "Indentation" msgstr "Indentação" #: ../code/dialogs/preferences-dialog.cpp:425 msgid "Home/End Behavior" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:426 msgid "File Saving" msgstr "Salvar Arquivo" #: ../code/dialogs/preferences-dialog.cpp:427 msgid "_Tab width:" msgstr "Larguda da _Tabulação" #: ../code/dialogs/preferences-dialog.cpp:429 msgid "Insert _spaces instead of tabs" msgstr "Inserir _espaços em vez de tabulações " #: ../code/dialogs/preferences-dialog.cpp:430 msgid "Enable automatic _indentation" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:431 msgid "Smart _home/end" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:432 msgid "Enable _automatic saving of documents" msgstr "Ativar _salvar documentos automaticamente" #: ../code/dialogs/preferences-dialog.cpp:434 msgid "Autosave interval in _minutes:" msgstr "Salvar automaticamente no intervalo de _minutos" #: ../code/dialogs/preferences-dialog.cpp:455 msgid "" "With this option enabled, Home/End keys move to first/last character before " "going to the start/end of the line." msgstr "" "Com esta opção habilitada, chaves Home/End movem-se para o primeiro/último " "caractereindo para o início/fim da linha." #: ../code/dialogs/preferences-dialog.cpp:525 msgid "Text Wrapping" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:526 msgid "Line Numbers" msgstr "Número de Linhas" #: ../code/dialogs/preferences-dialog.cpp:527 msgid "Current Line" msgstr "Linha Atual" #: ../code/dialogs/preferences-dialog.cpp:528 msgid "Right Margin" msgstr "Margem Direita" #: ../code/dialogs/preferences-dialog.cpp:529 msgid "Bracket Matching" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:530 msgid "Whitespace Display" msgstr "Mostrar espaços em branco" #: ../code/dialogs/preferences-dialog.cpp:531 msgid "Enable text wrapping" msgstr "Ativar quebra-linha" #: ../code/dialogs/preferences-dialog.cpp:532 msgid "Do not split words over two lines" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:533 msgid "Display line numbers" msgstr "Mostrar números de linhas" #: ../code/dialogs/preferences-dialog.cpp:534 msgid "Highlight current line" msgstr "Realçar linha atual" #: ../code/dialogs/preferences-dialog.cpp:535 msgid "Display right margin" msgstr "Mostrar margem direita" #: ../code/dialogs/preferences-dialog.cpp:536 msgid "Right margin at column:" msgstr "Margem direita na coluna" #: ../code/dialogs/preferences-dialog.cpp:537 msgid "Highlight matching bracket" msgstr "Fechar colchete correspondente" #: ../code/dialogs/preferences-dialog.cpp:589 msgid "Display no whitespace" msgstr "Não mostrar espaços em branco" #: ../code/dialogs/preferences-dialog.cpp:592 msgid "Display spaces" msgstr "Mostrar espaçamentos" #: ../code/dialogs/preferences-dialog.cpp:596 msgid "Display tabs" msgstr "Mostrar tabulações" #: ../code/dialogs/preferences-dialog.cpp:600 msgid "Display tabs and spaces" msgstr "Mostrar tabulações e espaços" #: ../code/dialogs/preferences-dialog.cpp:651 msgid "Toolbar" msgstr "Barra de Ferramentas" #: ../code/dialogs/preferences-dialog.cpp:652 msgid "Font" msgstr "Fontes" #: ../code/dialogs/preferences-dialog.cpp:653 msgid "Color Scheme" msgstr "Esquema de cores" #: ../code/dialogs/preferences-dialog.cpp:661 msgid "Show text only" msgstr "Mostrar apenas texto" #: ../code/dialogs/preferences-dialog.cpp:663 msgid "Show icons only" msgstr "Mostrar apenas ícones" #: ../code/dialogs/preferences-dialog.cpp:665 msgid "Show both icons and text" msgstr "Mostrar os ícones e texto" #: ../code/dialogs/preferences-dialog.cpp:667 msgid "Show text besides icons" msgstr "Mostrar texto além de ícones" #: ../code/dialogs/preferences-dialog.cpp:756 msgid "Trusted CAs" msgstr "CAs confiáveis" #: ../code/dialogs/preferences-dialog.cpp:757 msgid "Secure Connection" msgstr "Conexão Segura" #: ../code/dialogs/preferences-dialog.cpp:758 #, fuzzy msgid "Authentication" msgstr "Indentação" #: ../code/dialogs/preferences-dialog.cpp:759 msgid "Select a file containing trusted CAs" msgstr "Selecionar um arquivo contendo dados confiáveis CAs " #: ../code/dialogs/preferences-dialog.cpp:764 msgid "None" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:765 msgid "Use a certificate" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:767 msgid "Select a private key file" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:769 #: ../code/dialogs/preferences-dialog.cpp:776 #, fuzzy msgid "Create New..." msgstr "Criar Diretório..." #: ../code/dialogs/preferences-dialog.cpp:773 msgid "Select a certificate file" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:801 msgid "Use TLS if possible" msgstr "Usar TLS se possível" #: ../code/dialogs/preferences-dialog.cpp:804 msgid "Always use TLS" msgstr "Sempre usar TLS" #: ../code/dialogs/preferences-dialog.cpp:892 msgid "Error reading file:" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:916 msgid "2048-bit RSA private key is being generated, please wait..." msgstr "" #: ../code/dialogs/preferences-dialog.cpp:948 msgid "Select a location for the generated key" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:966 msgid "Select a location for the generated certificate" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:1068 msgid "Preferences" msgstr "Preferências" #: ../code/dialogs/preferences-dialog.cpp:1073 msgid "User" msgstr "Usuário" #: ../code/dialogs/preferences-dialog.cpp:1074 msgid "Editor" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:1075 msgid "View" msgstr "Exibir" #: ../code/dialogs/preferences-dialog.cpp:1076 msgid "Appearance" msgstr "Aparência" #: ../code/dialogs/preferences-dialog.cpp:1077 msgid "Security" msgstr "Segurança" #: ../code/main.cpp:194 msgid "Gobby options" msgstr "Opções do Gobby" #: ../code/main.cpp:194 msgid "Options related to Gobby" msgstr "Opções relacionadas do Gobby" #: ../code/main.cpp:199 msgid "Display version information and exit" msgstr "Exibir informação da versão e sair" #: ../code/main.cpp:206 msgid "Also start a new Gobby instance when there is one running already" msgstr "" #: ../code/main.cpp:214 msgid "Connect to given host on startup, can be given multiple times" msgstr "" #: ../code/main.cpp:215 msgid "HOSTNAME" msgstr "" #: ../code/operations/operation-delete.cpp:65 msgid "Removing node \"%1\"..." msgstr "Removendo nó \"%1\"..." #: ../code/operations/operation-delete.cpp:75 msgid "Failed to delete node \"%1\"" msgstr "Falha ao excluir nó \"%1\"" #: ../code/operations/operation-export-html.cpp:168 #, c-format msgid "written by: %s" msgstr "escrito por: %s" #: ../code/operations/operation-export-html.cpp:241 msgid "" msgstr "" #. %1$s is session name/hostname #. %2$s is path within the session #. %3$s is current date as formatted by %c, #. %4$s is a link to the gobby site, it must be present because #. we need to handle that manually to insert a hyperlink #. instead of just printf'ing it. #: ../code/operations/operation-export-html.cpp:254 #, c-format msgid "Document generated from %1$s:%2$s at %3$s by %4$s" msgstr "Documento gerado a partir de %1$s:%2$s at %3$s by %4$s" #: ../code/operations/operation-export-html.cpp:395 msgid "Participants" msgstr "Participantes" #: ../code/operations/operation-export-html.cpp:475 msgid "Exporting document \"%1\" to \"%2\" in HTML..." msgstr "Exportando o documento \"%1\" to \"%2\" em HTML..." #: ../code/operations/operation-export-html.cpp:536 msgid "Failed to export document \"%1\" to HTML" msgstr "Falha ao exportar documento \"%1\" para HTML" #: ../code/operations/operation-new.cpp:75 msgid "Creating directory \"%1\"..." msgstr "Criando diretório \"%1\"..." #: ../code/operations/operation-new.cpp:76 msgid "Creating document \"%1\"..." msgstr "Criação de documento \"%1\"..." #: ../code/operations/operation-new.cpp:88 msgid "Failed to create directory \"%1\"" msgstr "Falha ao criar diretório \"%1\"" #: ../code/operations/operation-new.cpp:89 msgid "Failed to create document \"%1\"" msgstr "Falha ao criar documento \"%1\"" #. Translators: This is the 8 bit encoding that is tried when #. autodetecting a file's encoding. #: ../code/operations/operation-open.cpp:38 msgid "ISO-8859-1" msgstr "" #: ../code/operations/operation-open.cpp:114 msgid "Opening document \"%1\"..." msgstr "Abrindo documento \"%1\"..." #: ../code/operations/operation-open.cpp:134 msgid "" "The directory into which the new document was supposed to be inserted has " "been removed" msgstr "O diretório no qual o suposto novo documento foi inseridofoi removido" #: ../code/operations/operation-open.cpp:323 msgid "The file contains data not in the specified encoding" msgstr "O arquivo não contém dados no encoding especificado" #: ../code/operations/operation-open.cpp:334 msgid "" "The file either contains data in an unknown encoding, or it contains binary " "data." msgstr "" "O arquivo também contém dados em uma codificação desconhecida, ou que contém " "o bináriodados." #: ../code/operations/operation-open-multiple.cpp:91 msgid "Parent folder was removed" msgstr "Pasta pai foi removida" #: ../code/operations/operation-open-multiple.cpp:193 msgid "Failed to open multiple documents" msgstr "Falha ao abrir vários documentos" #: ../code/operations/operation-save.cpp:97 msgid "Saving document \"%1\" to \"%2\"..." msgstr "Salvar documento \"%1\" de \"%2\"..." #: ../code/operations/operation-save.cpp:226 msgid "" "The document contains one or more characters that cannot be encoded in the " "specified character coding." msgstr "" "O documento contém um ou mais caracteres que não podem ser codificados " "nacodificação de caractere especificado." #: ../code/operations/operation-save.cpp:287 msgid "Failed to save document \"%1\"" msgstr "Falha ao salvar o documento \"%1\"" #: ../code/operations/operation-subscribe-path.cpp:33 msgid "Invalid path: \"%1\"" msgstr "" #: ../code/operations/operation-subscribe-path.cpp:45 #, fuzzy msgid "Invalid path component: \"%1\"" msgstr "Falha ao abrir o documento \"%1\"" #: ../code/operations/operation-subscribe-path.cpp:135 #: ../code/operations/operation-subscribe-path.cpp:200 #, fuzzy msgid "Subscribing to \"%1\"..." msgstr "Consultando \"%1\"..." #: ../code/operations/operation-subscribe-path.cpp:149 #: ../code/operations/operation-subscribe-path.cpp:212 #, fuzzy msgid "Failed to connect to \"%1\"" msgstr "Falha ao criar diretório \"%1\"" #: ../code/operations/operation-subscribe-path.cpp:164 msgid "URI scheme \"%1\" not supported" msgstr "" #: ../code/operations/operation-subscribe-path.cpp:193 #, fuzzy msgid "Connecting to \"%1\"..." msgstr "Conexão para \"%1\" falhou" #: ../code/operations/operation-subscribe-path.cpp:220 #: ../code/operations/operation-subscribe-path.cpp:319 #: ../code/operations/operation-subscribe-path.cpp:356 #: ../code/operations/operation-subscribe-path.cpp:452 #: ../code/operations/operation-subscribe-path.cpp:472 #, fuzzy msgid "Could not subscribe to \"%1\"" msgstr "Não foi possível resolver \"%1\"" #: ../code/operations/operation-subscribe-path.cpp:322 #: ../code/operations/operation-subscribe-path.cpp:358 #, fuzzy msgid "Path \"%1\" does not exist" msgstr "Dispositivo \"%1\" não existe" #: ../code/util/file.cpp:65 ../code/util/file.cpp:74 msgid "Could not create directory \"%1\": %2" msgstr "Não foi possível criar o diretório \"%1\": %2" #: ../code/util/uri.cpp:91 msgid "Device \"%1\" does not exist" msgstr "Dispositivo \"%1\" não existe" #: ../code/window.cpp:104 msgid "This Computer" msgstr "" #: ../code/window.cpp:129 msgid "Document Browser" msgstr "Navegador do documento" #: ../code/window.cpp:182 msgid "You are still connected to a session" msgstr "Você ainda está conectado a uma sessão" #: ../code/window.cpp:190 msgid "Do you want to close Gobby nevertheless?" msgstr "Você quer fechar Gobby?" #: ../code/window.cpp:196 msgid "C_ancel" msgstr "C_ancelar" #: ../gobby-0.5.desktop.in.h:1 msgid "Gobby" msgstr "" #: ../gobby-0.5.desktop.in.h:2 msgid "Collaborative Editor" msgstr "Editor colaborativo" #: ../gobby-0.5.desktop.in.h:3 msgid "Gobby Collaborative Editor (0.5)" msgstr "" #: ../gobby-0.5.desktop.in.h:4 msgid "Edit text files collaboratively" msgstr "Editar arquivos de texto de forma colaborativa" #, fuzzy #~ msgid "Failed to resolve \"%1\": %2" #~ msgstr "Não foi possível resolver \"%1\"" #~ msgid "Resolving \"%1\"..." #~ msgstr "Resolvendo \"%1\"..." po/zh_TW.po000066400000000000000000001102061304552132100130630ustar00rootroot00000000000000# Gobby Traditional Chinese translation. # Copyright (C) 2010 THE gobby'S COPYRIGHT HOLDER. # This file is distributed under the same license as the gobby package. # Wei-Lun Chao , 2011. # msgid "" msgstr "" "Project-Id-Version: gobby 0.4.94\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2014-08-23 13:43-0400\n" "PO-Revision-Date: 2010-06-26 17:11+0800\n" "Last-Translator: Wei-Lun Chao \n" "Language-Team: Chinese (traditional) \n" "Language: zh_TW\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" #: ../code/commands/auth-commands.cpp:196 #, fuzzy msgid "Password required" msgstr "必須輸入密碼" #: ../code/commands/auth-commands.cpp:214 msgid "Incorrect password" msgstr "" #: ../code/commands/browser-commands.cpp:288 msgid "Subscribing to %1..." msgstr "訂用 %1…" #: ../code/commands/browser-commands.cpp:301 msgid "Subscribing to chat on %1..." msgstr "訂用聊天於 %1…" #: ../code/commands/browser-commands.cpp:591 msgid "Subscription failed" msgstr "訂用失敗" #: ../code/commands/browser-context-commands.cpp:86 msgid "Could not find a location where to store the certificate" msgstr "" #: ../code/commands/browser-context-commands.cpp:153 msgid "_Remove" msgstr "" #: ../code/commands/browser-context-commands.cpp:188 #, fuzzy msgid "_Disconnect from Server" msgstr "連線到伺服器" #: ../code/commands/browser-context-commands.cpp:220 #, fuzzy msgid "Create _Account..." msgstr "建立文件(_C)…" #: ../code/commands/browser-context-commands.cpp:245 msgid "Create Do_cument..." msgstr "建立文件(_C)…" #: ../code/commands/browser-context-commands.cpp:280 #, fuzzy msgid "Create Di_rectory..." msgstr "建立目錄…" #: ../code/commands/browser-context-commands.cpp:293 msgid "_Open Document..." msgstr "開啟文件(_O)…" #: ../code/commands/browser-context-commands.cpp:312 msgid "_Permissions..." msgstr "" #: ../code/commands/browser-context-commands.cpp:324 msgid "D_elete" msgstr "刪除(_E)" #: ../code/commands/browser-context-commands.cpp:405 msgid "Choose a name for the directory" msgstr "選擇目錄名稱" #: ../code/commands/browser-context-commands.cpp:406 msgid "Choose a name for the document" msgstr "選擇文件名稱" #: ../code/commands/browser-context-commands.cpp:407 msgid "_Directory Name:" msgstr "目錄名稱(_D):" #: ../code/commands/browser-context-commands.cpp:408 msgid "_Document Name:" msgstr "文件名稱(_D):" #: ../code/commands/browser-context-commands.cpp:411 msgid "C_reate" msgstr "建立(_R)" #: ../code/commands/browser-context-commands.cpp:415 msgid "New Directory" msgstr "新目錄" #: ../code/commands/browser-context-commands.cpp:416 #: ../code/commands/file-tasks/task-new.cpp:36 msgid "New Document" msgstr "新文件" #: ../code/commands/browser-context-commands.cpp:436 #: ../code/commands/file-tasks/task-open-file.cpp:22 msgid "Choose a text file to open" msgstr "選擇要開啟的文字檔" #: ../code/commands/browser-context-commands.cpp:558 #: ../code/commands/browser-context-commands.cpp:569 msgid "Account successfully created" msgstr "" #: ../code/commands/browser-context-commands.cpp:562 msgid "When re-connecting to the server, the new account will be used." msgstr "" #: ../code/commands/browser-context-commands.cpp:573 msgid "" "The certificate has been stored at %1.\n" "\n" "To login to this account, set the certificate in Gobby's preferences and re-" "connect to the server." msgstr "" #: ../code/commands/browser-context-commands.cpp:586 #, fuzzy msgid "Failed to create account" msgstr "建立文件「%1」時失敗" #: ../code/commands/browser-context-commands.cpp:589 msgid "Could not save the certificate for the account: %1" msgstr "" #: ../code/commands/file-tasks/task-export-html.cpp:27 msgid "Choose a location to export document \"%1\" to" msgstr "選擇位置以匯出文件「%1」到" #: ../code/commands/file-tasks/task-open.cpp:46 msgid "Querying \"%1\"..." msgstr "正在查詢「%1」…" #: ../code/commands/file-tasks/task-open.cpp:103 #: ../code/operations/operation-open.cpp:474 #: ../code/operations/operation-open-multiple.cpp:180 msgid "Failed to open document \"%1\"" msgstr "開啟文件「%1」時失敗" #: ../code/commands/file-tasks/task-save.cpp:24 msgid "Choose a location to save document \"%1\" to" msgstr "選擇位置以儲存文件「%1」到" #. Help browser could not be invoked, show an error message to the user. #: ../code/commands/help-commands.cpp:70 msgid "There was an error displaying help." msgstr "出現了錯誤顯示說明。" #: ../code/commands/help-commands.cpp:93 msgid "Contributors:" msgstr "貢獻者:" #: ../code/commands/help-commands.cpp:99 msgid "British English:" msgstr "英語:" #: ../code/commands/help-commands.cpp:101 msgid "German:" msgstr "德語:" #: ../code/commands/help-commands.cpp:118 msgid "" "Permission to use, copy, modify, and/or distribute this software for any " "urpose with or without fee is hereby granted, provided that the above " "copyright notice and this permission notice appear in all copies.\n" "\n" "THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES " "WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF " "MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY " "SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES " "WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION " "OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN " "CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE." msgstr "" #: ../code/commands/subscription-commands.cpp:148 msgid "" "The connection to the publisher of this document has been lost. Further " "changes to the document could not be synchronized to others anymore, " "therefore the document cannot be edited anymore.\n" "\n" "Please note also that it is possible that not all of your latest changes " "have reached the publisher before the connection was lost." msgstr "" "已經失去對於這份文件出版者的連線。文件的進一步變更將無法再繼續同步到其他人," "因而文件無法再繼續編輯。\n" "\n" "請注意,也有可能在失去與出版者的連線之前,並非您所有的最後變更已送達。" #: ../code/commands/synchronization-commands.cpp:30 msgid "Synchronization in progress... %1%%" msgstr "同步化正在進行…%1%%" #. Document cannot be used if an error happened #. during synchronization. #: ../code/commands/synchronization-commands.cpp:42 msgid "This document cannot be used." msgstr "這個文件無法使用。" #: ../code/commands/synchronization-commands.cpp:45 #: ../code/commands/user-join-commands.cpp:43 msgid "" "If you have an idea what could have caused the problem, then you may attempt " "to solve it and try again (after having closed this document). Otherwise it " "is most likely a bug in the software. In that case, please file a bug report " "at http://gobby.0x539.de/trac/newticket and provide as much information as " "you can, including what you did when the problem occurred and how to " "reproduce the problem (if possible) so that we can fix the problem in a " "later version. Thank you." msgstr "" "如果您有某個可能造成此問題的想法,那麼您可以試圖解決它並再次嘗試 (於關閉這個" "文件之後)。否則很有可能是軟體中的錯誤。在這種情況下,請將錯誤回報給 http://" "gobby.0x539.de/trac/newticket 並儘可能提供足夠的資訊,包括當問題發生時您做了" "什麼事,以及如何重新產生問題 (如果可能) ,以便讓我們可以在稍後的版本中修正問" "題。謝謝您。" #: ../code/commands/synchronization-commands.cpp:59 msgid "Synchronization failed: %1" msgstr "同步化失敗:%1" #. TODO: Adjust this for chat sessions #: ../code/commands/user-join-commands.cpp:38 msgid "" "You can still watch others editing the document, but you cannot edit it " "yourself." msgstr "您仍然可以監看其他人編輯文件,但是您無法自行編輯它。" #: ../code/commands/user-join-commands.cpp:57 msgid "User Join failed: %1" msgstr "使用者加入失敗:%1" #: ../code/commands/user-join-commands.cpp:64 msgid "Permissions are not granted to modify the document." msgstr "" #: ../code/core/browser.cpp:111 msgid "_Direct Connection" msgstr "直接的連線(_D)" #: ../code/core/browser.cpp:113 msgid "Host Name:" msgstr "主機名稱:" #: ../code/core/certificatemanager.cpp:83 #, c-format msgid "Failed to write Diffie-Hellman parameters to \"%s\": %s" msgstr "" #: ../code/core/certificatemanager.cpp:276 #, c-format msgid "Failed to read Diffie-Hellman parameters: %s" msgstr "" #: ../code/core/certificatemanager.cpp:338 msgid "File does not contain a X.509 certificate" msgstr "" #: ../code/core/certificatemanager.cpp:414 msgid "Certificate does not belong to the chosen key" msgstr "" #: ../code/core/foldermanager.cpp:322 msgid "The document has been removed from the server." msgstr "" #: ../code/core/header.cpp:203 msgid "_None" msgstr "無(_N)" #: ../code/core/header.cpp:227 msgid "_File" msgstr "檔案(_F)" #: ../code/core/header.cpp:230 msgid "_Open..." msgstr "開啟(_O)…" #: ../code/core/header.cpp:233 msgid "Open _Location..." msgstr "開啟位置(_L)…" #: ../code/core/header.cpp:237 msgid "Save _As..." msgstr "另存新檔(_A)…" #: ../code/core/header.cpp:241 ../code/core/iconmanager.cpp:43 msgid "Save All" msgstr "全部儲存" #: ../code/core/header.cpp:241 msgid "Save all open files locally" msgstr "儲存所有開啟檔案於本機" #: ../code/core/header.cpp:243 msgid "Export As _HTML..." msgstr "匯出為 _HTML…" #: ../code/core/header.cpp:246 msgid "Connect _to Server..." msgstr "連線到伺服器(_T)…" #: ../code/core/header.cpp:251 msgid "_Edit" msgstr "編輯(_E)" #: ../code/core/header.cpp:259 msgid "_Find..." msgstr "尋找(_F)…" #: ../code/core/header.cpp:261 msgid "Find Ne_xt" msgstr "找下一個(_X)" #: ../code/core/header.cpp:262 msgid "Find next match of phrase searched for" msgstr "找下一個匹配的片語依據搜尋" #: ../code/core/header.cpp:265 msgid "Find Pre_vious" msgstr "找上一個(_V)" #: ../code/core/header.cpp:266 msgid "Find previous match of phrase searched for" msgstr "找上一個匹配的片語依據搜尋" #: ../code/core/header.cpp:271 msgid "Find and Rep_lace..." msgstr "尋找和置換(_L)…" #: ../code/core/header.cpp:275 msgid "Go to _Line..." msgstr "前往指定列(_L)…" #: ../code/core/header.cpp:279 msgid "Pr_eferences..." msgstr "偏好設定(_E)…" #: ../code/core/header.cpp:281 msgid "_View" msgstr "檢視(_V)" #: ../code/core/header.cpp:284 msgid "Reset User Colors" msgstr "重置使用者顏色" #: ../code/core/header.cpp:285 msgid "Hide user colors in current document" msgstr "在目前的文件中隱藏使用者顏色" #: ../code/core/header.cpp:289 msgid "Fullscreen" msgstr "" #: ../code/core/header.cpp:290 msgid "Show the editing window in fullscreen mode" msgstr "" #: ../code/core/header.cpp:294 msgid "Zoom In" msgstr "" #: ../code/core/header.cpp:297 msgid "Zoom Out" msgstr "" #: ../code/core/header.cpp:300 msgid "View Toolbar" msgstr "檢視工具列" #: ../code/core/header.cpp:301 msgid "Whether to show the toolbar" msgstr "是否要顯示工具列" #: ../code/core/header.cpp:305 msgid "View Statusbar" msgstr "檢視狀態列" #: ../code/core/header.cpp:306 msgid "Whether to show the statusbar" msgstr "是否要顯示狀態列" #: ../code/core/header.cpp:310 msgid "View Document Browser" msgstr "檢視文件瀏覽器" #: ../code/core/header.cpp:311 msgid "Whether to show the document browser" msgstr "是否要顯示文件瀏覽器" #: ../code/core/header.cpp:315 msgid "View Chat" msgstr "檢視聊天" #: ../code/core/header.cpp:316 msgid "Whether to show the chat pane" msgstr "是否要顯示聊天窗格" #: ../code/core/header.cpp:320 msgid "View Document User List" msgstr "檢視文件使用者清單" #: ../code/core/header.cpp:321 msgid "Whether to show the user list for documents" msgstr "是否要顯示文件的使用者清單" #: ../code/core/header.cpp:325 msgid "View Chat User List" msgstr "檢視聊天使用者清單" #: ../code/core/header.cpp:326 msgid "Whether to show the user list for the chat" msgstr "是否要顯示聊天的使用者清單" #: ../code/core/header.cpp:330 msgid "_Highlight Mode" msgstr "強調模式(_H)" #: ../code/core/header.cpp:336 msgid "_Help" msgstr "求助(_H)" #: ../code/core/header.cpp:339 msgid "_Contents" msgstr "內容(_C)" #: ../code/core/header.cpp:340 msgid "Opens the Gobby manual" msgstr "手動開啟 Gobby" #: ../code/core/header.cpp:343 msgid "_About..." msgstr "關於(_A)…" #: ../code/core/header.cpp:344 msgid "Shows Gobby's copyright and credits" msgstr "顯示 Gobby 的著作權和鳴謝" #: ../code/core/iconmanager.cpp:49 msgid "User list" msgstr "使用者清單" #: ../code/core/iconmanager.cpp:55 msgid "Document list" msgstr "文件清單" #: ../code/core/iconmanager.cpp:61 ../code/window.cpp:52 msgid "Chat" msgstr "聊天" #: ../code/core/iconmanager.cpp:68 msgid "User Color Indicator" msgstr "使用者顏色指示器" #: ../code/core/selfhoster.cpp:103 msgid "Generating 2048-bit Diffie-Hellman parameters..." msgstr "" #: ../code/core/selfhoster.cpp:135 #, fuzzy msgid "Failed to generate Diffie-Hellman parameters" msgstr "刪除節點「%1」時失敗" #: ../code/core/selfhoster.cpp:137 msgid "" "This means that Perfect Forward Secrecy (PFS) is not available. Restart " "Gobby to re-try generating the parameters. The specific error was:\n" "\n" "%1" msgstr "" #: ../code/core/selfhoster.cpp:262 msgid "" "In order to start sharing your documents, choose a private key and " "certificate or create a new pair in the preferences" msgstr "" #: ../code/core/selfhoster.cpp:282 #, fuzzy msgid "Failed to share documents" msgstr "儲存文件「%1」時失敗" #: ../code/core/sessionuserview.cpp:39 msgid "User List" msgstr "使用者清單" #: ../code/core/statusbar.cpp:418 msgid "Ln %1, Col %2\t%3" msgstr "列 %1, 行 %2\t%3" #: ../code/core/statusbar.cpp:421 msgid "OVR" msgstr "覆寫" #: ../code/core/statusbar.cpp:421 msgid "INS" msgstr "插入" #: ../code/core/textsessionview.cpp:638 msgid "Text written by %1" msgstr "文章由 %1 編寫" #: ../code/core/textsessionview.cpp:644 msgid "Unowned text" msgstr "無主的文章" #: ../code/core/userlist.cpp:108 msgid "Users" msgstr "使用者" #: ../code/dialogs/connection-dialog.cpp:26 msgid "Connect to Server" msgstr "連線到伺服器" #: ../code/dialogs/connection-dialog.cpp:30 msgid "Please enter a host name with which to establish a connection." msgstr "請輸入要與其建立連線的主機名稱。" #: ../code/dialogs/connection-dialog.cpp:32 msgid "_Remote Endpoint:" msgstr "遠端終點(_R):" #: ../code/dialogs/document-location-dialog.cpp:28 msgid "Document Name:" msgstr "文件名稱:" #: ../code/dialogs/document-location-dialog.cpp:155 msgid "Select document's target location" msgstr "選取文件目標位置" #: ../code/dialogs/document-location-dialog.cpp:157 msgid "Choose a directory to create the document into:" msgstr "選擇目錄以於其中建立文件:" #: ../code/dialogs/document-location-dialog.cpp:163 msgid "Select documents' target location" msgstr "選取文件目標位置" #: ../code/dialogs/document-location-dialog.cpp:165 msgid "Choose a directory to create the documents into:" msgstr "選擇目錄以於其中建立文件:" #: ../code/dialogs/find-dialog.cpp:44 ../code/dialogs/find-dialog.cpp:142 msgid "Find" msgstr "尋找" #: ../code/dialogs/find-dialog.cpp:47 msgid "_Search for:" msgstr "搜尋(_S):" #: ../code/dialogs/find-dialog.cpp:49 msgid "Replace _with:" msgstr "置換成(_W):" #: ../code/dialogs/find-dialog.cpp:51 msgid "_Match case" msgstr "匹配大小寫(_M)" #: ../code/dialogs/find-dialog.cpp:52 msgid "Match _entire word only" msgstr "匹配完整字詞(_E)" #: ../code/dialogs/find-dialog.cpp:53 msgid "Search _backwards" msgstr "向後搜尋(_B)" #: ../code/dialogs/find-dialog.cpp:54 msgid "Wra_p around" msgstr "回到開始部份(_P)" #: ../code/dialogs/find-dialog.cpp:100 msgid "Replace _All" msgstr "全部置換(_A)" #: ../code/dialogs/find-dialog.cpp:102 msgid "_Replace" msgstr "置換(_R)" #: ../code/dialogs/find-dialog.cpp:142 msgid "Replace" msgstr "置換" #: ../code/dialogs/find-dialog.cpp:161 ../code/dialogs/find-dialog.cpp:177 msgid "Phrase \"%1\" has not been found" msgstr "片語「%1」尚未找到" #: ../code/dialogs/find-dialog.cpp:335 msgid "No occurrence has been replaced" msgstr "沒有置換任何項目" #: ../code/dialogs/find-dialog.cpp:341 msgid "%1 occurrence has been replaced" msgid_plural "%1 occurrences have been replaced" msgstr[0] "已經置換 %1 個項目" #: ../code/dialogs/goto-dialog.cpp:26 msgid "Go to line" msgstr "前往指定列" #: ../code/dialogs/goto-dialog.cpp:29 msgid "Line _number:" msgstr "列號(_N):" #: ../code/dialogs/goto-dialog.cpp:50 msgid "Go To _Line" msgstr "前往指定列(_L)" #: ../code/dialogs/initial-dialog.cpp:76 msgid "Generating 2048-bit RSA private key..." msgstr "" #: ../code/dialogs/initial-dialog.cpp:99 #, fuzzy msgid "Failed to generate private key" msgstr "刪除節點「%1」時失敗" #: ../code/dialogs/initial-dialog.cpp:102 msgid "" "%1\n" "\n" "You can try again to create a key in the Security tab of the preferences " "dialog." msgstr "" #: ../code/dialogs/initial-dialog.cpp:141 msgid "Failed to generate self-signed certificate" msgstr "" #: ../code/dialogs/initial-dialog.cpp:144 msgid "" "%1\n" "\n" "You can try again to create a certificate in the Security tab of the " "preferences dialog." msgstr "" #: ../code/dialogs/initial-dialog.cpp:198 msgid "Choose a user color" msgstr "選擇使用者顏色" #: ../code/dialogs/initial-dialog.cpp:204 msgid "Welcome to Gobby" msgstr "歡迎使用 Gobby" #: ../code/dialogs/initial-dialog.cpp:216 msgid "" "Before we start, a few options need to be configured. You can later change " "them by choosing Edit ▸ Preferences from the menu." msgstr "" "在開始之前,需要組配幾個選項。您可以稍後藉由從選單選擇「編輯▸偏好設定」來變更" "它們。" #: ../code/dialogs/initial-dialog.cpp:224 msgid "User Name" msgstr "使用者名稱" #: ../code/dialogs/initial-dialog.cpp:226 msgid "Your name as shown to other users." msgstr "您顯示給其他使用者看的名稱。" #: ../code/dialogs/initial-dialog.cpp:235 msgid "User Color" msgstr "使用者顏色" #: ../code/dialogs/initial-dialog.cpp:237 msgid "The color with which text you have written is branded." msgstr "用來標示您所寫文字的顏色。" #: ../code/dialogs/initial-dialog.cpp:256 #, fuzzy msgid "Remote Connections" msgstr "安全連線" #: ../code/dialogs/initial-dialog.cpp:262 #: ../code/dialogs/preferences-dialog.cpp:217 msgid "Allow remote users to edit local documents" msgstr "" #: ../code/dialogs/initial-dialog.cpp:268 msgid "Ask remote users for a password" msgstr "" #: ../code/dialogs/initial-dialog.cpp:273 #: ../code/dialogs/preferences-dialog.cpp:220 #, fuzzy msgid "Password:" msgstr "伺服器密碼(_P):" #: ../code/dialogs/initial-dialog.cpp:297 msgid "No authentication (Not recommended)" msgstr "" #: ../code/dialogs/initial-dialog.cpp:304 msgid "" "Don't authenticate ourselves to remote users. Data transfer will be " "unencrypted." msgstr "" #: ../code/dialogs/initial-dialog.cpp:313 msgid "Create a self-signed certificate (Recommended)" msgstr "" #: ../code/dialogs/initial-dialog.cpp:321 msgid "" "It may take a minute or two until remote users can connect while the " "security certificate is being generated." msgstr "" #: ../code/dialogs/initial-dialog.cpp:331 msgid "Use an existing certificate (Expert option)" msgstr "" #: ../code/dialogs/initial-dialog.cpp:338 msgid "" "Use an existing private key and certificate for authentication. The files " "must be in PEM format." msgstr "" #: ../code/dialogs/initial-dialog.cpp:347 #: ../code/dialogs/preferences-dialog.cpp:766 msgid "Private key:" msgstr "" #: ../code/dialogs/initial-dialog.cpp:349 #: ../code/dialogs/preferences-dialog.cpp:772 msgid "Certificate:" msgstr "" #: ../code/dialogs/open-location-dialog.cpp:26 msgid "Open Location" msgstr "開啟位置" #: ../code/dialogs/open-location-dialog.cpp:27 msgid "Enter the _location (URI) of the file you would like to open:" msgstr "輸入您想要開啟的檔案位置(URI)(_L):" #: ../code/dialogs/password-dialog.cpp:25 msgid "Password Required" msgstr "必須輸入密碼" #: ../code/dialogs/password-dialog.cpp:31 msgid "Connection to host \"%1\" requires a password." msgstr "連線到主機「%1」需要密碼。" #: ../code/dialogs/password-dialog.cpp:32 msgid "Invalid password for host \"%1\". Please try again." msgstr "密碼對於主機「%1」無效。請再試一次。" #: ../code/dialogs/password-dialog.cpp:35 msgid "Server _Password:" msgstr "伺服器密碼(_P):" #: ../code/dialogs/preferences-dialog.cpp:198 msgid "Settings" msgstr "設定值" #: ../code/dialogs/preferences-dialog.cpp:199 #, fuzzy msgid "Remote Users" msgstr "重置使用者顏色" #: ../code/dialogs/preferences-dialog.cpp:200 #, fuzzy msgid "Local Documents" msgstr "新文件" #: ../code/dialogs/preferences-dialog.cpp:202 msgid "User name:" msgstr "使用者名稱:" #: ../code/dialogs/preferences-dialog.cpp:204 msgid "User color:" msgstr "使用者顏色:" #: ../code/dialogs/preferences-dialog.cpp:206 msgid "Color intensity:" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:208 msgid "Choose a new user color" msgstr "選擇新的使用者顏色" #: ../code/dialogs/preferences-dialog.cpp:209 msgid "Show cursors of remote users" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:210 msgid "Show selections of remote users" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:212 #, fuzzy msgid "Highlight current line of remote users" msgstr "強調目前的列" #: ../code/dialogs/preferences-dialog.cpp:214 msgid "Indicate cursor position of remote users in the scrollbar" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:219 msgid "Require remote users to enter a password" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:222 msgid "Port:" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:226 msgid "Remember local documents after Gobby restart" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:227 #, fuzzy msgid "Local documents directory:" msgstr "主機目錄:" #: ../code/dialogs/preferences-dialog.cpp:423 msgid "Tab Stops" msgstr "定位停駐點" #: ../code/dialogs/preferences-dialog.cpp:424 msgid "Indentation" msgstr "縮排" #: ../code/dialogs/preferences-dialog.cpp:425 msgid "Home/End Behavior" msgstr "Home/End 按鍵行為" #: ../code/dialogs/preferences-dialog.cpp:426 msgid "File Saving" msgstr "檔案儲存" #: ../code/dialogs/preferences-dialog.cpp:427 msgid "_Tab width:" msgstr "定位點寬度(_T):" #: ../code/dialogs/preferences-dialog.cpp:429 msgid "Insert _spaces instead of tabs" msgstr "插入空格以代替跳格(_S)" #: ../code/dialogs/preferences-dialog.cpp:430 msgid "Enable automatic _indentation" msgstr "啟用自動縮排(_I)" #: ../code/dialogs/preferences-dialog.cpp:431 msgid "Smart _home/end" msgstr "智慧型 _Home/End" #: ../code/dialogs/preferences-dialog.cpp:432 msgid "Enable _automatic saving of documents" msgstr "啟用文件自動儲存(_A)" #: ../code/dialogs/preferences-dialog.cpp:434 msgid "Autosave interval in _minutes:" msgstr "自動儲存間隔以分鐘為單位(_M):" #: ../code/dialogs/preferences-dialog.cpp:455 msgid "" "With this option enabled, Home/End keys move to first/last character before " "going to the start/end of the line." msgstr "" "如果啟用這個選項,Home/End 按鍵會在前往列首/列尾之前移動到第一/最後一個字元。" #: ../code/dialogs/preferences-dialog.cpp:525 msgid "Text Wrapping" msgstr "文字環繞" #: ../code/dialogs/preferences-dialog.cpp:526 msgid "Line Numbers" msgstr "列號" #: ../code/dialogs/preferences-dialog.cpp:527 msgid "Current Line" msgstr "目前的列" #: ../code/dialogs/preferences-dialog.cpp:528 msgid "Right Margin" msgstr "右邊界" #: ../code/dialogs/preferences-dialog.cpp:529 msgid "Bracket Matching" msgstr "中括號匹配" #: ../code/dialogs/preferences-dialog.cpp:530 msgid "Whitespace Display" msgstr "空白顯示" #: ../code/dialogs/preferences-dialog.cpp:531 msgid "Enable text wrapping" msgstr "啟用文字環繞" #: ../code/dialogs/preferences-dialog.cpp:532 msgid "Do not split words over two lines" msgstr "不分割單字於兩列" #: ../code/dialogs/preferences-dialog.cpp:533 msgid "Display line numbers" msgstr "顯示列號" #: ../code/dialogs/preferences-dialog.cpp:534 msgid "Highlight current line" msgstr "強調目前的列" #: ../code/dialogs/preferences-dialog.cpp:535 msgid "Display right margin" msgstr "顯示右邊界" #: ../code/dialogs/preferences-dialog.cpp:536 msgid "Right margin at column:" msgstr "右邊界於行數:" #: ../code/dialogs/preferences-dialog.cpp:537 msgid "Highlight matching bracket" msgstr "強調匹配中括號" #: ../code/dialogs/preferences-dialog.cpp:589 msgid "Display no whitespace" msgstr "不顯示任何空格" #: ../code/dialogs/preferences-dialog.cpp:592 msgid "Display spaces" msgstr "顯示空格" #: ../code/dialogs/preferences-dialog.cpp:596 msgid "Display tabs" msgstr "顯示跳格" #: ../code/dialogs/preferences-dialog.cpp:600 msgid "Display tabs and spaces" msgstr "顯示跳格和空格" #: ../code/dialogs/preferences-dialog.cpp:651 msgid "Toolbar" msgstr "工具列" #: ../code/dialogs/preferences-dialog.cpp:652 msgid "Font" msgstr "字型" #: ../code/dialogs/preferences-dialog.cpp:653 msgid "Color Scheme" msgstr "配色方案" #: ../code/dialogs/preferences-dialog.cpp:661 msgid "Show text only" msgstr "只顯示文字" #: ../code/dialogs/preferences-dialog.cpp:663 msgid "Show icons only" msgstr "只顯示圖示" #: ../code/dialogs/preferences-dialog.cpp:665 msgid "Show both icons and text" msgstr "顯示圖示和文字" #: ../code/dialogs/preferences-dialog.cpp:667 msgid "Show text besides icons" msgstr "在圖示旁邊顯示文字" #: ../code/dialogs/preferences-dialog.cpp:756 msgid "Trusted CAs" msgstr "信賴的 CAs" #: ../code/dialogs/preferences-dialog.cpp:757 msgid "Secure Connection" msgstr "安全連線" #: ../code/dialogs/preferences-dialog.cpp:758 #, fuzzy msgid "Authentication" msgstr "縮排" #: ../code/dialogs/preferences-dialog.cpp:759 msgid "Select a file containing trusted CAs" msgstr "選取包含信賴的 CAs 檔案" #: ../code/dialogs/preferences-dialog.cpp:764 #, fuzzy msgid "None" msgstr "無(_N)" #: ../code/dialogs/preferences-dialog.cpp:765 msgid "Use a certificate" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:767 msgid "Select a private key file" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:769 #: ../code/dialogs/preferences-dialog.cpp:776 #, fuzzy msgid "Create New..." msgstr "建立文件(_C)…" #: ../code/dialogs/preferences-dialog.cpp:773 msgid "Select a certificate file" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:801 msgid "Use TLS if possible" msgstr "儘量使用 TLS" #: ../code/dialogs/preferences-dialog.cpp:804 msgid "Always use TLS" msgstr "一律使用 TLS" #: ../code/dialogs/preferences-dialog.cpp:892 msgid "Error reading file:" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:916 msgid "2048-bit RSA private key is being generated, please wait..." msgstr "" #: ../code/dialogs/preferences-dialog.cpp:948 msgid "Select a location for the generated key" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:966 msgid "Select a location for the generated certificate" msgstr "" #: ../code/dialogs/preferences-dialog.cpp:1068 msgid "Preferences" msgstr "偏好設定" #: ../code/dialogs/preferences-dialog.cpp:1073 msgid "User" msgstr "使用者" #: ../code/dialogs/preferences-dialog.cpp:1074 msgid "Editor" msgstr "編輯器" #: ../code/dialogs/preferences-dialog.cpp:1075 msgid "View" msgstr "檢視" #: ../code/dialogs/preferences-dialog.cpp:1076 msgid "Appearance" msgstr "外觀" #: ../code/dialogs/preferences-dialog.cpp:1077 msgid "Security" msgstr "安全" #: ../code/main.cpp:194 msgid "Gobby options" msgstr "Gobby 選項" #: ../code/main.cpp:194 msgid "Options related to Gobby" msgstr "與 Gobby 相關的選項" #: ../code/main.cpp:199 msgid "Display version information and exit" msgstr "顯示版本資訊然後離開" #: ../code/main.cpp:206 msgid "Also start a new Gobby instance when there is one running already" msgstr "當已經有一個執行中的 Gobby 時,也要開啟新的實體" #: ../code/main.cpp:214 msgid "Connect to given host on startup, can be given multiple times" msgstr "啟動時連線到給定的主機,可以多次給定" #: ../code/main.cpp:215 msgid "HOSTNAME" msgstr "主機名稱" #: ../code/operations/operation-delete.cpp:65 msgid "Removing node \"%1\"..." msgstr "移除節點「%1」…" #: ../code/operations/operation-delete.cpp:75 msgid "Failed to delete node \"%1\"" msgstr "刪除節點「%1」時失敗" #: ../code/operations/operation-export-html.cpp:168 #, c-format msgid "written by: %s" msgstr "由 %s 所編寫" #: ../code/operations/operation-export-html.cpp:241 msgid "" msgstr "<無法印出日期>" #. %1$s is session name/hostname #. %2$s is path within the session #. %3$s is current date as formatted by %c, #. %4$s is a link to the gobby site, it must be present because #. we need to handle that manually to insert a hyperlink #. instead of just printf'ing it. #: ../code/operations/operation-export-html.cpp:254 #, c-format msgid "Document generated from %1$s:%2$s at %3$s by %4$s" msgstr "文件產生自 %1$s:%2$s 於 %3$s 由 %4$s" #: ../code/operations/operation-export-html.cpp:395 msgid "Participants" msgstr "參加者" #: ../code/operations/operation-export-html.cpp:475 msgid "Exporting document \"%1\" to \"%2\" in HTML..." msgstr "以 HTML 匯出文件「%1」到「%2」…" #: ../code/operations/operation-export-html.cpp:536 msgid "Failed to export document \"%1\" to HTML" msgstr "以 HTML 匯出文件「%1」時失敗" #: ../code/operations/operation-new.cpp:75 msgid "Creating directory \"%1\"..." msgstr "建立目錄「%1」…" #: ../code/operations/operation-new.cpp:76 msgid "Creating document \"%1\"..." msgstr "建立文件「%1」…" #: ../code/operations/operation-new.cpp:88 msgid "Failed to create directory \"%1\"" msgstr "建立目錄「%1」時失敗" #: ../code/operations/operation-new.cpp:89 msgid "Failed to create document \"%1\"" msgstr "建立文件「%1」時失敗" #. Translators: This is the 8 bit encoding that is tried when #. autodetecting a file's encoding. #: ../code/operations/operation-open.cpp:38 msgid "ISO-8859-1" msgstr "UTF-8" #: ../code/operations/operation-open.cpp:114 msgid "Opening document \"%1\"..." msgstr "開啟文件「%1」…" #: ../code/operations/operation-open.cpp:134 msgid "" "The directory into which the new document was supposed to be inserted has " "been removed" msgstr "預計用來插入新文件的目錄已被移除" #: ../code/operations/operation-open.cpp:323 msgid "The file contains data not in the specified encoding" msgstr "檔案含有不屬於指定編碼的資料" #: ../code/operations/operation-open.cpp:334 msgid "" "The file either contains data in an unknown encoding, or it contains binary " "data." msgstr "檔案若非含有不明編碼的資料,就是含有二進位資料。" #: ../code/operations/operation-open-multiple.cpp:91 msgid "Parent folder was removed" msgstr "上層資料夾被移除了" #: ../code/operations/operation-open-multiple.cpp:193 msgid "Failed to open multiple documents" msgstr "開啟多份文件時失敗" #: ../code/operations/operation-save.cpp:97 msgid "Saving document \"%1\" to \"%2\"..." msgstr "儲存文件「%1」到「%2」…" #: ../code/operations/operation-save.cpp:226 msgid "" "The document contains one or more characters that cannot be encoded in the " "specified character coding." msgstr "文件含有一或多個不屬於指定字元編碼的字元。" #: ../code/operations/operation-save.cpp:287 msgid "Failed to save document \"%1\"" msgstr "儲存文件「%1」時失敗" #: ../code/operations/operation-subscribe-path.cpp:33 msgid "Invalid path: \"%1\"" msgstr "" #: ../code/operations/operation-subscribe-path.cpp:45 #, fuzzy msgid "Invalid path component: \"%1\"" msgstr "開啟文件「%1」時失敗" #: ../code/operations/operation-subscribe-path.cpp:135 #: ../code/operations/operation-subscribe-path.cpp:200 #, fuzzy msgid "Subscribing to \"%1\"..." msgstr "訂用 %1…" #: ../code/operations/operation-subscribe-path.cpp:149 #: ../code/operations/operation-subscribe-path.cpp:212 #, fuzzy msgid "Failed to connect to \"%1\"" msgstr "建立目錄「%1」時失敗" #: ../code/operations/operation-subscribe-path.cpp:164 msgid "URI scheme \"%1\" not supported" msgstr "" #: ../code/operations/operation-subscribe-path.cpp:193 #, fuzzy msgid "Connecting to \"%1\"..." msgstr "連線到「%1」失敗" #: ../code/operations/operation-subscribe-path.cpp:220 #: ../code/operations/operation-subscribe-path.cpp:319 #: ../code/operations/operation-subscribe-path.cpp:356 #: ../code/operations/operation-subscribe-path.cpp:452 #: ../code/operations/operation-subscribe-path.cpp:472 #, fuzzy msgid "Could not subscribe to \"%1\"" msgstr "無法解析「%1」" #: ../code/operations/operation-subscribe-path.cpp:322 #: ../code/operations/operation-subscribe-path.cpp:358 #, fuzzy msgid "Path \"%1\" does not exist" msgstr "裝置「%1」不存在" #: ../code/util/file.cpp:65 ../code/util/file.cpp:74 msgid "Could not create directory \"%1\": %2" msgstr "無法建立目錄「%1」:%2" #: ../code/util/uri.cpp:91 msgid "Device \"%1\" does not exist" msgstr "裝置「%1」不存在" #: ../code/window.cpp:104 msgid "This Computer" msgstr "" #: ../code/window.cpp:129 msgid "Document Browser" msgstr "文件瀏覽器" #: ../code/window.cpp:182 msgid "You are still connected to a session" msgstr "您仍然與作業階段連線" #: ../code/window.cpp:190 msgid "Do you want to close Gobby nevertheless?" msgstr "您無論如何都要關閉 Gobby 嗎?" #: ../code/window.cpp:196 msgid "C_ancel" msgstr "取消(_A)" #: ../gobby-0.5.desktop.in.h:1 msgid "Gobby" msgstr "Gobby" #: ../gobby-0.5.desktop.in.h:2 msgid "Collaborative Editor" msgstr "共同編輯器" #: ../gobby-0.5.desktop.in.h:3 msgid "Gobby Collaborative Editor (0.5)" msgstr "Gobby 共同編輯器 (0.5)" #: ../gobby-0.5.desktop.in.h:4 msgid "Edit text files collaboratively" msgstr "共同編輯文字檔" #~ msgid "" #~ "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.\n" #~ "\n" #~ "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.\n" #~ "\n" #~ "You should have received a copy of the GNU General Public License along " #~ "with this program; if not, write to the Free Software Foundation, Inc., " #~ "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA" #~ msgstr "" #~ "這個程式是自由軟體;您可以遵循由自由軟體基金會所發布的 GNU 通用公共授權來" #~ "再次散布它和/或修改它;無論您依據的是本授權的第二版,或 (您自行選擇的) 任" #~ "何後續的版本。\n" #~ "\n" #~ "發行本程式是希望該它能夠有用, 但是沒有任何擔保;對於某一特定目的之適售性" #~ "與適用性亦無暗示性的擔保。參看 GNU 通用公共授權以獲得更多細節。\n" #~ "\n" #~ "您應該已收到隨附於這個程式的 GNU 通用公共授權複本;若無則請寫信到 Free " #~ "Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA " #~ "02110-1301, USA" #~ msgid "User Join in progress..." #~ msgstr "使用者正在加入…" #, fuzzy #~ msgid "Failed to resolve \"%1\": %2" #~ msgstr "無法解析「%1」" #~ msgid "Resolving \"%1\"..." #~ msgstr "正在解析「%1」…" #~ msgid "Paths" #~ msgstr "路徑" #~ msgid "" #~ "The directory into which locally hosted sessions are permanently stored" #~ msgstr "本機寄存作業階段永久儲存於其中的目錄" update-potfiles000077500000000000000000000005051304552132100141050ustar00rootroot00000000000000#!/bin/sh for file in code/resources/ui/*.ui; do intltool-extract --type="gettext/glade" $file sed -i -e 's/char \*s = //' ${file}.h git add ${file}.h done grep --binary-files=without-match "_(" code -R | \ sed -e 's/:.*//' | uniq | sort > po/POTFILES.in echo gobby-0.5.desktop.in >> po/POTFILES.in win32/000077500000000000000000000000001304552132100120145ustar00rootroot00000000000000win32/Makefile.am000066400000000000000000000003121304552132100140440ustar00rootroot00000000000000if WIN32 win32/gobby.res: win32/gobby.rc $(WINDRES) -i win32/gobby.rc --input-format=rc -o win32/gobby.res -O coff BUILT_SOURCES += win32/gobby.res endif EXTRA_DIST += win32/gobby.ico win32/gobby.rc win32/gobby.ico000066400000000000000000000226761304552132100136270ustar00rootroot0000000000000000 %(0` &!(:;97: 4:;;;;:70#######################&7D;;;;;;;7& JJJLLLNOOQQQTTTSSSOOOKKKHHHJJJMMMNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNKKKABB;<<::: ! Y<;;8- %|||YmmmJJJ___STSJMJAFAAFAJMJSTS\\\qqqEFF[4% ZZZ***&8O"d"$l$&r&(x((x(&r&$m$"f"U>6<><{{{PPP5VVV#!(u(////////////////////////////++1\\\+tttT1@@>><<884411////////////////////B\\\+O}}}VVV???+YYWWTTOOJJCC;;33////////////////<Tn\\\+AAA5+"PC6(& kkqqmmhhaaZZPPGG<<22//////////..#1#c}7U}\\\+ lYFoXs[r[XJC~R`.-+&} [7bbbtu[f*;.-("wCU AAAFMIX.-+%{ _7" ]]]:u7SSS{y{P...6+1r'2m