[Xfce4-commits] <mousepad:master> Merge branch 'nick_0_3'
Matthew Brush
noreply at xfce.org
Sat May 5 21:32:05 CEST 2012
Updating branch refs/heads/master
to dafbfc876426ecc221a353fd586488a35e063a9f (commit)
from 9de1554965033cc24b641be3aae84b8fbd1e5bab (commit)
commit dafbfc876426ecc221a353fd586488a35e063a9f
Merge: 9de1554 551173d
Author: Matthew Brush <matt at xfce.org>
Date: Sat Mar 31 13:32:13 2012 -0700
Merge branch 'nick_0_3'
This branch contains a complete re-write.
commit 551173dcfac7ec4ae8c7e0a2baa4ea147c92253a
Author: Matthew Brush <matt at xfce.org>
Date: Sat Mar 31 13:24:21 2012 -0700
Add .gitignore file
commit 4783ee11ebd8b90e70923d83a7468351d6fedb80
Author: Matthew Brush <matt at xfce.org>
Date: Fri Oct 7 05:58:04 2011 -0700
Fix a couple minor issues from last couple commits.
commit 34cb31bbed2aef45bf0f85d34a87aea9d4eb0846
Author: Matthew Brush <matt at xfce.org>
Date: Fri Oct 7 05:45:14 2011 -0700
Add filetype selection popup menu to statusbar.
commit adf4ea822a98376362f812ae1ff6488148218380
Author: Matthew Brush <matt at xfce.org>
Date: Fri Oct 7 03:24:38 2011 -0700
Update a few documentation files.
commit ab93fe61fe7317e61ee6ec804e3d266bc1e50980
Author: Matthew Brush <matt at xfce.org>
Date: Fri Oct 7 02:15:21 2011 -0700
Make Filetype separate in the Document menu.
commit 4777a405afb12e26853328a9131c2c3f131014ec
Author: Matthew Brush <matt at xfce.org>
Date: Fri Oct 7 01:05:09 2011 -0700
Reorder action entry arrays (forgotten in last commit).
commit 143b6ba6c2a8e024057bfca7e96deeb6bc3e765f
Author: Matthew Brush <matt at xfce.org>
Date: Fri Oct 7 00:52:59 2011 -0700
Cleanup View and Document menus.
* Move "Line Numbers" from Document to View menu and apply it to all
of the currently open textviews.
* Re-order View and Document menu items, mostly sorted alphabetically.
* Remove extra separators between related items.
* Re-arrange action handler functions to be in the same order as their
items appear in the XML file.
commit 5c88d180a1e20cb51737990948bb041529a97357
Author: Matthew Brush <matt at xfce.org>
Date: Thu Oct 6 22:37:00 2011 -0700
Add extra settings to custom print dialog tab.
* Add line number increment setting.
* Add separate font settings for header, body and line numbers.
commit ba85e8dae9ae1c478242b3c678389191dd8e1e87
Author: Matthew Brush <matt at xfce.org>
Date: Thu Oct 6 19:35:30 2011 -0700
Use GtkSourcePrintCompositor property names in config file.
commit c45ed8da1ae2ed17fb291084cebae1275d33f7c8
Author: Matthew Brush <matt at xfce.org>
Date: Thu Oct 6 19:23:13 2011 -0700
Switch to GtkSourcePrintCompositor for printing.
* Add printing option for syntax highlighting.
* Some of the page footer code implemented in #if 0 blocks.
* Remove mousepad_print_end_print() function.
commit 029bef60746206eaf08dc11c89afd59db186db8d
Merge: 05b3c46 52e5c6a
Author: Matthew Brush <matt at xfce.org>
Date: Thu Oct 6 04:05:33 2011 -0700
Merge branch 'nick_0_3' into gtksourceview
commit 52e5c6a0051cd456802d6b50b85b9190a33d62cf
Author: Matthew Brush <matt at xfce.org>
Date: Thu Oct 6 03:56:42 2011 -0700
Prevent segfault when opening more that 100 files.
Reported by Mark_T on #xfce-dev IRC.
commit 05b3c46c8b9877fa62b07fd6a04b1b82fc5576ed
Author: Matthew Brush <matt at xfce.org>
Date: Thu Oct 6 03:11:18 2011 -0700
Select filetype radio menu item when detecting filetype.
Fix setting filetype to None.
commit 66a2d472174195507aa00ae8287ff4c1b62c6604
Author: Matthew Brush <matt at xfce.org>
Date: Thu Oct 6 01:35:14 2011 -0700
Cleanup colour scheme util functions a bit.
commit 5d74b7018dd0f821f6f37a0293868466a9b836b3
Author: Matthew Brush <matt at xfce.org>
Date: Wed Oct 5 20:50:06 2011 -0700
Change mousepad_util_color_schemes_get_sorted to use GSList.
Also add some comments and set default value on selected_color_scheme in
mousepad_window_menu_color_schemes.
commit 81eb018689978502e6a128436ed4cb8ab5de77cf
Author: Matthew Brush <mbrush at codebrainz.ca>
Date: Wed Oct 5 15:59:11 2011 -0700
Select the last saved colour scheme in the menu on application start.
commit 6ceb27df8efb7b23acc73c147511721cbea45b20
Author: Matthew Brush <mbrush at codebrainz.ca>
Date: Wed Oct 5 15:49:10 2011 -0700
Cleanup and refactor some code from the last few commits.
* Move colour scheme and language utility functions to mousepad-util.[ch].
* Rename colour scheme and language utility functions with better names.
* Put colour scheme action func/proto in the correct order of appearance.
commit 92b22bcb38d66f63b5ce69d9df7d3f14e60ac15a
Author: Matthew Brush <mbrush at codebrainz.ca>
Date: Wed Oct 5 15:05:43 2011 -0700
Add menu to manually select filetype/language.
This code needs to be reviewed and cleaned up more.
Thanks to "cavalier" on #xfce-dev IRC for help getting this working.
commit c5ac3ceeff8979a20d60e40f62f0b04d0ea67b9f
Author: Matthew Brush <mbrush at codebrainz.ca>
Date: Tue Oct 4 01:28:34 2011 -0700
Noise: remove extra whitespace added by editor.
Apologies for the noise.
commit 7be1dde6dfe47b08cf49f6bfb3f4fe031d9fad8e
Author: Matthew Brush <mbrush at codebrainz.ca>
Date: Tue Oct 4 00:08:50 2011 -0700
Use better string compare function for colour scheme names.
commit 349066029e579a6dc1014aff3759e0f78ec52eb7
Author: Matthew Brush <mbrush at codebrainz.ca>
Date: Mon Oct 3 23:47:52 2011 -0700
Fix kludge from last commit (FIXME comment).
Add a new signal 'language-changed' to MousepadDocument for the window
to use to get notified of when to update the language/filetype in the
statusbar.
commit ba38b4940b2998c30ba9bde6c707bf1c589cae0b
Author: Matthew Brush <mbrush at codebrainz.ca>
Date: Mon Oct 3 22:33:51 2011 -0700
Update statusbar filetype when notebook pages are changed.
Note the FIXME in the comments, this will probably need some refactoring.
commit 82b6e834ddeea4be8bf901307e6e093bde24631c
Author: Matthew Brush <mbrush at codebrainz.ca>
Date: Mon Oct 3 21:48:03 2011 -0700
Add initial filetype/language support and guess on file open.
Still need to add UI elements to let user select language to use.
commit cc401c48985b9f06c855cac39175ddcbe0efe091
Author: Matthew Brush <mbrush at codebrainz.ca>
Date: Mon Oct 3 20:06:24 2011 -0700
Add colour scheme support.
* Add new preference 'color-scheme' that stores the scheme's ID string.
* Add new 'Color Schemes' menu and placeholder for items to UI XML file.
* Add new actions for all colour schemes and merge into UI manager menu.
* Handle actions by looping though all textview widgets and setting style.
* Using 'g_str_hash' of scheme id for radio action value which might be bad.
commit 9a6ad0cb703ec1ef475458cec656cbeef9d865dc
Author: Matthew Brush <mbrush at codebrainz.ca>
Date: Mon Oct 3 08:10:59 2011 -0700
Remove old tab size code and use GtkSourceView's tab-width.
commit 728cb754e087cf0bbc2efa7167a8008b2c66752a
Author: Matthew Brush <mbrush at codebrainz.ca>
Date: Mon Oct 3 06:54:20 2011 -0700
Remove undo manager code and use GtkSourceView's undo manager.
There was a call to mousepad_undo_lock() in mousepad-encoding-dialog.c
that needs to be checked, since I wasn't sure what that call was for.
commit 6be5f6c5f9de3242f234f08506da31e55396d9b5
Author: Matthew Brush <mbrush at codebrainz.ca>
Date: Mon Oct 3 02:38:25 2011 -0700
Remove old insert spaces code and use GtkSourceView's.
Remove special tab handling since GtkSourceView handles it.
commit 898612c83495635ef657fea60f6b9048640b0f15
Author: Matthew Brush <mbrush at codebrainz.ca>
Date: Mon Oct 3 02:01:16 2011 -0700
Remove old auto-indentation code and use GtkSourceView's auto-indent.
commit 801eadc860c894420a1807e5a9f496895bd031c8
Author: Matthew Brush <mbrush at codebrainz.ca>
Date: Mon Oct 3 01:52:27 2011 -0700
Remove old line number code and use GtkSourceView's show-line-numbers.
commit d1970d6b3ec4e212f8fd2823b6f5dd00c17fe4cf
Author: Matthew Brush <mbrush at codebrainz.ca>
Date: Mon Oct 3 01:34:42 2011 -0700
Make MousepadView a subclass of GtkSourceView instead of GtkTextView.
Add include for gtksourceview.h in mousepad-private.h.
Causes the line numbers to always be shown initially but this should be
fixed once the line numbers code is ported to use GtkSourceView.
commit 4e5c46f563dc7e468041a62f82cab8e8bfc4a3dc
Author: Matthew Brush <mbrush at codebrainz.ca>
Date: Mon Oct 3 05:30:31 2011 -0700
Add GtkSourceView dependency to build system.
Still need to determine which version of GtkSourceView to depend on.
commit b401a34b8c6b88ebd26e26f03a70a13548cbc4c0
Author: Matthew Brush <mbrush at codebrainz.ca>
Date: Mon Oct 3 05:20:18 2011 -0700
Prevent warnings when messing around with statusbar internal widgets.
Remove statusbar frame border.
commit 6bdfbdf87e4ac5ffd528b6daafd9755acfe04415
Author: Matthew Brush <mbrush at codebrainz.ca>
Date: Mon Oct 3 03:56:58 2011 -0700
Revert "Drop usage of the GtkStatusbar."
This reverts commit 185821ae262235222f7ef1d3a86dc0ea2b19bbd9.
commit ff0faf3c3250b4e56598aec891109d96f3aece7a
Author: Nick Schermer <nick at xfce.org>
Date: Fri Oct 15 16:24:59 2010 +0200
Fix error in recent actions.
commit 185821ae262235222f7ef1d3a86dc0ea2b19bbd9
Author: Nick Schermer <nick at xfce.org>
Date: Fri Oct 15 16:19:40 2010 +0200
Drop usage of the GtkStatusbar.
Only shows a lot of errors. Because of this we loose the
resize grip for now, but in gtk3 this will be possible again.
commit 385535209b957746bec3d29818bc1d4490df35e3
Author: Nick Schermer <nick at xfce.org>
Date: Thu Aug 26 21:11:41 2010 +0200
Move MousepadHelp to $libdir/xfce4/mousepad.
commit a269c2d3c2f009925da47191f3eb7fec459072c1
Author: Nick Schermer <nick at xfce.org>
Date: Wed Apr 28 02:14:14 2010 +0200
Rename configure.in.in to configure.ac.in.
commit acfa674ea1984c5a449cf8e23ae53e4409edda5a
Author: Nick Schermer <nick at xfce.org>
Date: Sat Apr 24 10:40:26 2010 +0200
Drop old po files.
commit 3eea02f5adfabad3bbd9975eb6f69fea1c213a1b
Author: Nick Schermer <nick at xfce.org>
Date: Sat Apr 24 10:35:51 2010 +0200
Make build GIT friendly.
Also apply silent rules and use new XDT macros.
commit 1972a36499a3644bc32067bb748a79f0d4523257
Author: Nick Schermer <nick at xfce.org>
Date: Sat Apr 24 10:31:47 2010 +0200
Don't disable deprecated Gtk functions.
commit c5c4bd8a3f8c8a0ce94e806e027d68dac540cec0
Author: Nick Schermer <nick at xfce.org>
Date: Wed Feb 10 12:59:50 2010 +0100
Fix compilation with pango.
commit 375a246bf9f38733f368bfa05163734119b54168
Author: Nick Schermer <nick at xfce.org>
Date: Wed Aug 12 18:01:02 2009 +0200
Move in all the changes from the old git branch.
Because individual patches did not apply, this is one big
commit the moving everything at once.
commit d7fce4e8e9890bee3cde1d8afa549b320ca0bb4c
Author: Mike Massonnet <mmassonnet at xfce.org>
Date: Tue Apr 1 07:11:50 2008 +0000
Revert French translation. Add a note inside TRANSLATORS
(Old svn revision: 26701)
commit 1a06210de348a7ef5e54262a43dd0410e4b91f62
Author: Mike Massonnet <mmassonnet at xfce.org>
Date: Mon Mar 31 11:46:16 2008 +0000
mousepad/nick: update french translation
(Old svn revision: 26700)
commit 73c4f9cde2db6826c690d2b19eab715bd493c704
Author: Nick Schermer <nick at xfce.org>
Date: Mon Mar 10 19:52:22 2008 +0000
* mousepad/mousepad-document.c: Swap the colors of readonly and
modified tabs. Red is modified and green readonly.
(Old svn revision: 26674)
commit e6deae974c002619ee11c3d68d92ec768b235ac5
Author: Nick Schermer <nick at xfce.org>
Date: Sun Mar 2 13:51:40 2008 +0000
* mousepad/mousepad-file.c: Don't set an error and return false on
externally modified when the file does not exist. This fixes
an error when saving a new file.
* mousepad/mousepad-file.c: Emit signal when the readonly status
changes.
* mousepad/mousepad-document.c: Use the readonly signal to update
the label color. This fixes a readonly-colored label when saving
a new file.
(Old svn revision: 26656)
commit 790cd28e036aec098aaee8cc03b101f0c436580d
Author: Nick Schermer <nick at xfce.org>
Date: Sun Feb 17 12:08:21 2008 +0000
* mousepad/mousepad-search-bar.c: Select the text in the search
entry when focussing the search bar (Ctrl+F).
* mousepad/mousepad-window.c: Set default keybindings of Find Next
and Find Previous to F3 and Shft+F3.
(Old svn revision: 26621)
commit 59d55f4852cdfaf811f51c97927defdb1e197d5b
Author: Nick Schermer <nick at xfce.org>
Date: Tue Jan 15 18:23:21 2008 +0000
* configure.in.in, mousepad/Makefile.am: Remove gmodule from the
library list, is not used. Add glib to make sure it's linked.
* mousepad/mousepad-window.c: Use the HOME environment variable
to search for the templates path, fallback on g_get_homedir.
* MousepadHelp.in: Improve script to find other browsers too,
instead of only using exo-open.
* mousepad/mousepad-preferences: Improve the performace of loading
and saving a bit. Loading now directly writes to the internal
value array. Could be a bit tricky, we'll see.
* mousepad/mousepad-{preferences,print}.c: Use g_key_file_{get,
set}_value instead of g_key_file_{get,set}_string, should be a
bit faster.
* mousepad/mousepad-window.c: Be more secure when loading tab sizes.
* po/mousepad.pot: Update.
(Old svn revision: 26574)
commit 9b4a2525a341187a8ea4bddfaeff3e07ef724773
Author: Nick Schermer <nick at xfce.org>
Date: Tue Jan 15 10:59:07 2008 +0000
* mousepad/mousepad-window.c: Update menu actions after a document
is closed, so the detach action becomes insensitive when there
is only one document openened.
(Old svn revision: 26573)
commit 35ea349ad0c4ee3557c3c28bb7b5c30cb9fdfe22
Author: Nick Schermer <nick at xfce.org>
Date: Tue Jan 15 10:34:47 2008 +0000
* TODO: Update
* mousepad/mousepad-{dialogs,window,file}.c: Check for external
file modifications before saving.
* ChangeLog: Update.
* configure.in.in: Remove api version and add support for a nano
version.
(Old svn revision: 26572)
commit 2709fd15751d4197f8c260989b7069473dfae65f
Author: Nick Schermer <nick at xfce.org>
Date: Mon Jan 14 16:49:57 2008 +0000
* Drop libxfce4util as dependency
* Switch to accessories-text-editor as icon name
(Old svn revision: 26571)
commit a8924e11dad4f268f699ce42abd348ba4dcb2492
Author: Nick Schermer <nick at xfce.org>
Date: Mon Jan 14 11:33:17 2008 +0000
* Use G_DEFINE_TYPE.
* Properly set the emission stage for all signals.
* Remove some unused code.
* Fix assert when dropping a file in the editor window.
* Small typo in the menu tooltips.
(Old svn revision: 26570)
commit b93f219eb75efb3769f5d1a150831a4a1f09291d
Author: Nick Schermer <nick at xfce.org>
Date: Wed Dec 12 09:29:50 2007 +0000
* mousepad/mousepad-window.c: Try to find the file encoding in the
recent history, when opening a document that didn't pass the
UTF-8 check.
(Old svn revision: 26460)
commit ccf616594ecd39b49bb632372c7cd28557a776f1
Author: Nick Schermer <nick at xfce.org>
Date: Mon Dec 10 19:47:45 2007 +0000
* mousepad/mousepad-undo.c: Remove testing messages.
(Old svn revision: 26455)
commit d1075d569a475bc2e16dfaab0209e1e4d0c8dec0
Author: Nick Schermer <nick at xfce.org>
Date: Mon Dec 10 19:36:58 2007 +0000
* mousepad/mousepad-window.c: Use the switch-page signal instead
of a notify on the page property.
* mousepad/mousepad-*: Remove deprecated tooltip api when compiled
with Gtk+ >= 2.12.
* mousepad/mousepad-replace-dialog.c: Connect tab switch signal for
updating the dialog status when switching tabs.
* mousepad/mousepad-undo.c: Fix issues with the undo manager. It
now works with a points system (chars: 1pt, space/tab: 10pts,
new line: 25pts). A step contains 30pts, whole words and spaces
are merged. This way the undo steps feel more consistent.
Properly keep the number of visible undo steps < 100.
Store document save point in the undo manager, when you undo to
this points the document will not be modified, but the history
is not erased either when saving.
* mousepad/Makefile.am: Add DGTK_DISABLE_DEPRECATED and
DGDK_DISABLE_DEPRECATED.
(Old svn revision: 26454)
commit 1d7bef769029aae39ddcecb6c529177393363dcf
Author: Nick Schermer <nick at xfce.org>
Date: Sat Dec 8 10:46:08 2007 +0000
* mousepad/mousepad-{dialog,window}.c: Show save as button in
question dialog for readonly documents. Also add the modified
readonly documents to the save-as queue when running save all.
(Old svn revision: 26451)
commit f67680befa81f562943b591a7143e25baea1fb17
Author: Nick Schermer <nick at xfce.org>
Date: Sat Dec 8 10:31:50 2007 +0000
* mousepad/mousepad-{search-bar,window}.c: Remove highlight when
hiding the search bar. Search ahead when opening the search bar.
(Old svn revision: 26450)
commit 72415680ea141554b03f505d5f9a96861957c5a0
Author: Nick Schermer <nick at xfce.org>
Date: Fri Dec 7 19:42:23 2007 +0000
* mousepad/mousepad-search-bar.c: Fix red entry when toggling the
highlight button.
(Old svn revision: 26447)
commit 562a27b0824e528841b1a58b01d8c9978f4bd05e
Author: Nick Schermer <nick at xfce.org>
Date: Thu Dec 6 20:11:21 2007 +0000
* mousepad/mousepad-document.c: Fix column number in statusbar.
First column is 0 and not 1.
* mousepad/mousepad-dialogs.c: Improve the go to dialog. You can now
set the column number too.
* mousepad/mousepad-window{.c,-ui.xml}: Improve the menu layout big
time. Make mnemonics consistent right now. A couple of name,
tooltip and keybindings changes. Internal names make more sense
now.
* mousepad/mousepad-{window,view}.c Add option to paste from
history. The history holds the last 9 history cut/copied items
from Mousepad.
* mousepad/mousepad-window.c: New from template menu. Works the
same as the implementation in Thunar. Menu is generated the first
time the file menu is shown. Same for the recent menu.
* mousepad/mousepad-{window,view}.c: Add option to toggle between
column and normal selections. Put it in View -> Change to Column
Selection. To make this work properly I've removed multi
selection. Code got much easier and a couple of complex functions
were dropped.
* mousepad/mousepad-window{.c,-ui.xml}: Put search menu items in
the edit menu, since you will most likely access them from
keybindings most of the time.
* mousepad/mousepad-window.c: Cleanuped up a lot of functions.
* mousepad/mousepad-window.c: Check if files exists when generating
the recent menu, remove it when it was not found. Also unref the
action after adding it to the action group, fixes an ugly memory
leak, since the recent info isn't released eighter.
* mousepad/mousepad-util.c: Add faster function to escape
underscores in the recent menu. Drop the previous function used
for this.
* mousepad/mousepad-private.h: Add macros around g_object_(get/set)
_qdata and use them instead of the existing g_object_(get/set)
_data functions.
* mousepad/mousepad-view.c: Fix indent with (shift) tab.
* mousepad/mousepad-view.c: Improve line number drawing. Only
redraw the line numbers in the expose area and some other changes
to reduce the amount of redraws and iter calls.
* mousepad/mousepad-view.c: Use the IM to type in multi selections,
the previous way was deprecated and this fixes a couple of weird
issues.
* mousepad/mousepad-view.c: Show realtime selection size in column
selections.
* mousepad/mousepad-{document,view,window}.c: Put selection change
in a separate signal to avoid multiple update the the action
group.
* mousepad/mousepad-view.c: Avoid a lot of statusbar updates and
column selection redraws during dragging by comparing the old and
new cursor position. On the other hand, the 'only draw the visible
area during drag' trick has been partly removed to properly
display the selection length during a column draw that covers
more then the window height.
* mousepad/mousepad-{util,view}.c: Add functions to change the case
of a selection.
* mousepad/mousepad-view.c: Add function to replace tabs with spaces
and vice versa. The replacements are inlined, so you don't see
visual changes in the document. Actions are not usable during
column selections.
* mousepad/mousepad-window.c: Make the arrow buttons in the notebook
work.
* mousepad/mousepad-window.c: Close document on middle click on the
tab, this is a stupid feature, but since everyone requests it on
apps with tabs: add it to save a bugzilla report.
* mousepad/mousepad-{window,view}.c: Add actions to increase or
decrease the indentation of line(s) using the menu or keybindings.
* mousepad/mousepad-{window,view}.c: Add an action to duplicate a
line or selection. Only work for normal selections or no
selection.
* mousepad/mousepad-{window,view}.c: Add an action to move the
selected lines up and down. Not implemented for column selections
(yet). Menu actions are insensitive when there is no regual
selection.
* mousepad/mousepad-{window,view}.c: Add option to strip trailing
spaces and tabs.
* mousepad/mousepad-window.c: Add a file to the recent history
after saving it under another name.
* mousepad/mousepad-window{.c,-ui.xml}: Add line ending type in the
document menu.
* mousepad/mousepad-window.c: Avoid one menu update when adding or
removing a new document to the window.
* mousepad/mousepad-document.c: Change tab label color when the
document is modified or readonly.
* ChangeLog: Properly break lines.
(Old svn revision: 26441)
commit 976c9707d60945030612eaf79db2e577135fe4ef
Author: Nick Schermer <nick at xfce.org>
Date: Thu Nov 29 19:20:44 2007 +0000
* mousepad/mousepad-document.c: Make tab close button a bit smaller.
(Old svn revision: 26407)
commit d2cf9f1d53f4818ea92e162cf6b71b9963144ce8
Author: Nick Schermer <nick at xfce.org>
Date: Thu Nov 29 18:29:46 2007 +0000
* mousepad/mousepad-window.c: Use a box for packing the window elements.
A table was not really needed, and boxes are a bit faster in calculating
child sizes.
* mousepad/mousepad-window.c: Remove unused variable.
(Old svn revision: 26406)
commit debc440d2c428b5aa91dcb567fd96a4f50a2804c
Author: Nick Schermer <nick at xfce.org>
Date: Thu Nov 29 18:00:22 2007 +0000
* mousepad/mousepad-print.c: Use bold attribute for labels, so there
is no html in the strings.
(Old svn revision: 26405)
commit f3a364cda15d48093802adbb4190a1d027e6a102
Author: Nick Schermer <nick at xfce.org>
Date: Thu Nov 29 16:38:43 2007 +0000
* mousepad/mousepad-utils.c: Add functions that extent the default
Gtk word start/end function. Use them in the search functions,
transpose and double click selection.
* mousepad/mousepad-view.c: Implement transpose for multi selections.
* mousepad/mousepad-view.c: Improve word swap function.
* docs/manual/C/Mousepad.xml.in: Other layout of the help file. Content
will follow.
* mousepad/mousepad-{document,window,view}.c: Show the selection length
in the statusbar. Also merged two signals and a couple of functions.
* mousepad/mousepad-window.c: Change default keybinding of new document
(Ctrl+N) and new window (Shift+Ctrl+N). Assign Ctrl+T to transpose.
* mousepad/mousepad-window.c: Update the window title after saving a
file with a new name.
* mousepad/mousepad-file.c: Set readonly to FALSE when starting with
a non-existing filename in the argument.
* mousepad/mousepad-print.c: Improve the print backend. Settings are
now saved. You can set the paper type, toggle options for line
numbers, wrap and page heading and use a custom font.
(Old svn revision: 26404)
commit 03cd4c266c5d539c85dbe22f505d1d54c1b011b4
Author: Nick Schermer <nick at xfce.org>
Date: Fri Oct 26 15:02:40 2007 +0000
* mousepad/mousepad-view.c: Implement transpose:
- Selection on one line: Inverse selected text.
- Multiple lines selected: Invert seleted lines.
- Cursor is inside a word: Swap chars on each side of the cursor.
- Cursor is outside a word: Swap word left and right of the cursor.
- Cursor at the start of a line: Swap line with the line above.
- Cursor at the end of a line: Swap line with the line below.
Will add support for multi- and column-selections later. Thanks to
Textmate for this great idea.
(Old svn revision: 26200)
commit ec4cae752aa01e235b870df476b0c3008cb24f4d
Author: Nick Schermer <nick at xfce.org>
Date: Fri Oct 26 08:43:19 2007 +0000
* MousepadHelp.in, mousepad/mousepad-{dialogs,window}.{c,h}:
Add support for the help file.
* Mousepad.spec.in: Add spec file.
(Old svn revision: 26199)
commit 673f739167aa23710a7b570eb7e863871fd09e4d
Author: Nick Schermer <nick at xfce.org>
Date: Thu Oct 25 20:46:06 2007 +0000
* mousepad/mousepad-view.c: Redraw selection on theme change.
(Old svn revision: 26197)
commit 24c163061c3e40620db488f98c51f1edd8d20f62
Author: Nick Schermer <nick at xfce.org>
Date: Thu Oct 25 17:51:07 2007 +0000
* mousepad/mousepad-view.c: Reimplemented multi-selection. It is also
possible to do multi editing (typing in all the selections), select
random words in the document (they will be copied to the clipboard as
a list of words) and multi insert (create a drag without content or
drag at the end of lines). Multiple selection will also be merged
when possible. This proably has some rough edges, but it works pretty
good. Keyboard multi drags will be added later.
* docs/manual/C/Mousepad.xml.in: Add a bit of info about multiple- and
column-selections, but I'm not very good at this.
* mousepad/mousepad-{window.c,window-ui.xml}: Use our edit menu on right
click, so copy/paste/delete works with multi selections. Also saves a
bit of code since it integrates with the ui-manager.
(Old svn revision: 26195)
commit 2d4c6469fc7680fe83c02af5ecfd4791f96de79f
Author: Nick Schermer <nick at xfce.org>
Date: Tue Oct 23 08:57:10 2007 +0000
* configure.in.in, po/POTFILES.in: Fix dist-check. Thank to Brian
for the hint.
* mousepad/mousepad-{dialogs,window}.c: Implement tab size menu. You
can set the default tab sizes in the rc file (MiscDefaultTabSizes).
* mousepad/mousepad-window{-ui.xml,.c}: Reorder menus a bit. Go menu
is now called 'Navigation' and the 'go to line' item is added to this
menu. The document menu contains the tab size menu from now on.
* Rename some functions and vars to more suitable names.
(Old svn revision: 26178)
commit 0960eed1341ae9789caafcb61500fa2006a12a67
Author: Nick Schermer <nick at xfce.org>
Date: Mon Oct 22 11:16:59 2007 +0000
* configure.in.in: Make sure __OPTIMIZE__ is enable in normal builds.
* mousepad/mousepad-private.h: Tune G_LIKELY macros for pure boolean.
(Old svn revision: 26175)
commit 765cea4b137c29bbb1abebad9440a4a9534c93d7
Author: Nick Schermer <nick at xfce.org>
Date: Mon Oct 22 09:18:55 2007 +0000
* mousepad/mousepad-window.c: Idea of the previous commit was good,
but the implementation wasn't. Fix that.
(Old svn revision: 26174)
commit 02326c575f8bc002d5a9680c1bb9b2d4b474125d
Author: Nick Schermer <nick at xfce.org>
Date: Sun Oct 21 19:08:51 2007 +0000
* mousepad/mousepad-window.c: Make the recent manager a singleton
in the application, this is also done in gtk 2.12. We also
initialize the recent manager when needed, since it's quite
expensive to create one. This should improve the startup speed
a bit.
(Old svn revision: 26173)
commit c68eede22f8717c245154f83dab5b6569fc45d13
Author: Nick Schermer <nick at xfce.org>
Date: Sun Oct 21 15:17:17 2007 +0000
* docs/: Import basic manual files.
* configure.in.in, mousepad/Makefile.am: Don't search for startup-
notification and gtk-unix-print. We don't use this in the code.
(Old svn revision: 26172)
commit 1f30cae6c6774f25728d5c7254e772281762abd6
Author: Nick Schermer <nick at xfce.org>
Date: Thu Oct 18 09:47:52 2007 +0000
* mousepad/mousepad-view.c: Respect input methods and don't insert
text when the textview is not editable.
* mousepad/mousepad-{file,document,window}.c: Properly handle
read-only files. A file is now always readonly unless proven
otherwise.
(Old svn revision: 26145)
commit b01842db991e385a4a2373da1eaac1191197d9af
Author: Nick Schermer <nick at xfce.org>
Date: Thu Oct 18 09:04:31 2007 +0000
* mousepad/mousepad-utils.{c,h}: Add iter function to move the iter
in front of text. Code used from one of the indentation functions.
* mousepad/mousepad-view.c: Add code for a smart home button: when
the cursor starts a line and the home button is pressed, it will
move to the start of the text.
* mousepad/mousepad-view.c: Key bindings Ctrl + {Home,End} to jump
to the start and end of a document.
(Old svn revision: 26144)
commit a75d9920d838e508c10dfd17908233d45dc20e6c
Author: Nick Schermer <nick at xfce.org>
Date: Wed Oct 17 18:53:22 2007 +0000
* mousepad/mousepad-window.c: Cleanup some code and get rid of the
multiple action groups.
(Old svn revision: 26142)
commit 2d17e63c32d75fb9fb7f98e8660b4980ec24af7d
Author: Nick Schermer <nick at xfce.org>
Date: Wed Oct 17 11:02:46 2007 +0000
* mousepad/mousepad-window.c: Decrease the menu lock when a window
is closed and disconnect the recent manager handler (this is a
bug since 2.12 because the manager is a floating object).
(Old svn revision: 26141)
commit 8a4f2f2e71d3fa064295bbbe0ca4697e562878f3
Author: Nick Schermer <nick at xfce.org>
Date: Wed Oct 17 09:48:30 2007 +0000
* mousepad/mousepad-{window,util}.c: Fix compiler warnings.
(Old svn revision: 26140)
commit 73fe06eded04e6c604a49caf443cf3cc7d22ddbf
Author: Nick Schermer <nick at xfce.org>
Date: Tue Oct 16 20:06:10 2007 +0000
* Previous commit was a typo.
(Old svn revision: 26136)
commit 0c494023ae1504211afbba57c9c469e4955426dc
Author: Nick Schermer <nick at xfce.org>
Date: Tue Oct 16 20:03:00 2007 +0000
* Set Id property on files.
(Old svn revision: 26135)
commit d1196e9f4486f9a5d4610fb88a1076a5e8d476a5
Author: Nick Schermer <nick at xfce.org>
Date: Tue Oct 16 20:01:37 2007 +0000
* Diffstat:
38 files changed, 5987 insertions(+), 3632 deletions(-)
* mousepad/mousepad-dialogs.c: Set the correct default return
in the jump dialog.
* mousepad/mousepad-replace-{dialog,window,preferences}.{c,h}:
Implement the new replace dialog.
* Remove options from the type-ahead bar, it was too bloated
and those options are now available in the replace dialog.
* mousepad/mousepad-document.c: The MousepadDocument now holds
utf-8 valid names, the MousepadFile the real filename.
* mousepad/mousepad-window.c: Full tab dnd (detach) with
Gtk+ 2.12.
* mousepad/mousepad-encoding-dialog.{c,h}: Add a encoding dialog.
This dialog should help users to find the right encoding for a
document.
* mousepad/mousepad-print.{c,h}: Initial version of a basic
printing support. Needs some big improvement tho.
* mousepad/mousepad-statusbar: Fix issues with some theme engines
that paint a line above the statusbar (instead of a frame).
* mousepad/mousepad-window.c: A window is now destoyed when it
contains no tabs, previously this caused some segfaults with the
tab dnd code, but this is all fixed now.
* mousepad/mousepad-preferences.c: The option names are now stored
in the nick name of the pspec. With debug build this name is
compared with the option name from spec name to check for typos.
* mousepad/mousepad-view.c: Options for 'insert tab as spaces' and
settings the tab size. This also needed a rewrite of the
indentation code, which ended up much cleaner.
* mousepad/mousepad-view.c: Big speed improvements in the vertical
selction code.
* mousepad/mousepad-view.c: Cleanups in the clipboard code and move
code from mousepad-document to mousepad-view.
* mousepad/mousepad-{window,search-bar}.c: Handle clipboard actions
properly when the search bar is focused.
* A lot of code cleanups, bug fixes, polishing and stuff I can't
remember after 5 months ;).
(Old svn revision: 26134)
commit 772eeca118e48c94c5fe908c333197b3b6b8845e
Author: Nick Schermer <nick at xfce.org>
Date: Tue May 22 19:50:01 2007 +0000
* *.*: Remove copyright lines.
(Old svn revision: 25747)
commit c0bf115da5b4dd9ca9be101014e41565ffdeae1c
Author: Nick Schermer <nick at xfce.org>
Date: Sun May 20 18:44:10 2007 +0000
* mousepad/mousepad-{preferences,search-bar,document}.c,
mousepad/mousepad-types.h: Add match whole word option.
(Old svn revision: 25736)
commit 0f9ccf029632a9bb61f393243c1153fb58dd5793
Author: Nick Schermer <nick at xfce.org>
Date: Sun May 20 12:13:07 2007 +0000
* mousepad/Makefile.am: Use the new exo-csource
--strip-comments and --strip-content arguments.
(Old svn revision: 25729)
commit b408c7b5f735db6c91e3381953e6c5cfca967901
Author: Nick Schermer <nick at xfce.org>
Date: Sun May 20 09:58:48 2007 +0000
* mousepad/mousepad-view.c: Initialize variables, so gcc is
happy.
* README: Add some lines.
* mousepad/mousepad-window.c: Ctrl + Shift + v is the shortcut
to paste in a column.
(Old svn revision: 25726)
commit 924ec4cfdac8ef8ad541d643f9789836bb3bd86c
Author: Nick Schermer <nick at xfce.org>
Date: Thu May 17 20:00:53 2007 +0000
* mousepad/mousepad-view.c: Cleanup the line number code.
This version is a bit faster and removed a bunch of code.
* mousepad/mousepad-view.c: Cleanup the indentation code. You
can also increase the (vertical) selected text with Shift +
Space and decrease with Shift + Backspace. I though this
might be useful for developers.
* mousepad/mousepad-view.c: Vertical selection using the mouse
and Ctrl + Shift. Keyboard vertical selection is not possible
because that adds too much code for (hardly) nothing. You can
(un-)indent the block with Tab, Shift + Tab and the 2 new
space (in/de)crease commands above.
* mousepad/mousepad-view.c: Draw a vertical line to separate
the line numbers from the text, this looks beter with light
themes (IMHO).
* mousepad/mousepad-window.c: Add 'paste column' option to
paste the clipboard text in a column under the cursor.
* mousepad/mousepad-window.c: Set stock menu names to NULL so
Gtk fills the default (translated) name, makes your binary
smaller and translators happy.
* mousepad/mousepad-{search-bar,document}.c: Fix segfault with
empty string in the search bar and highlighting enabled.
* TODO: Add items.
(Old svn revision: 25720)
commit 4abd78f00bd8306fd7a8028875cb9050b62df53e
Author: Nick Schermer <nick at xfce.org>
Date: Sat May 12 16:38:47 2007 +0000
* mousepad/mousepad-window.c: Add extra tests if the file
really exists, because Gtk file dialogs hang if the
file does not exists.
(Old svn revision: 25708)
commit d2c3c1f88384b617269bcd129f6cce6a86b5efa2
Author: Nick Schermer <nick at xfce.org>
Date: Sat May 12 15:41:04 2007 +0000
* mousepad/mousepad-window.c: Jump to active file when
opening a new document.
(Old svn revision: 25707)
commit 23c7b01c285078e6346e1b65afcb611273ce3c2f
Author: Nick Schermer <nick at xfce.org>
Date: Thu May 10 16:13:39 2007 +0000
* mousepad/mousepad-undo.c: Don't store a string (or even
prepend it in a GString) when the user is inserting text.
This saves a whole bunch of relocations (Bug #2737). We also
flush the insert buffer after a redo and don't copy strings
when inverting a delete step. This should bring the memory
usage of the undo manager to a minimum.
* TODO: Add some undo manager reminders.
* mousepad/mousepad-window.c: Fix compiler warning when
debugging is enabled.
* mousepad/mousepad-application.c: Change from append to
prepend.
(Old svn revision: 25700)
commit a4f297b08aa19d2aa06c969d821af3590a231a2a
Author: Nick Schermer <nick at xfce.org>
Date: Tue May 8 17:17:37 2007 +0000
* mousepad/mousepad-window.c: Rename function so it matches
the standard mousepad_window_* names.
(Old svn revision: 25688)
commit 9fbd9fdc5d6123223d4ffe75a3407f44425c9548
Author: Nick Schermer <nick at xfce.org>
Date: Tue May 8 17:14:34 2007 +0000
* mousepad/mousepad-window.c: Open a new tab when the notebook
is double clicked. Also fix some indentation.
(Old svn revision: 25687)
commit 932f662654e9f024422e8fd806a8b9b90cc6149a
Author: Nick Schermer <nick at xfce.org>
Date: Tue May 1 21:16:15 2007 +0000
* configure.in.in: Don't break on a broken glibc.
(Old svn revision: 25664)
commit 5cd70d2665de061f5c64d6a7a21b5f564eeec813
Author: Nick Schermer <nick at xfce.org>
Date: Tue May 1 16:07:57 2007 +0000
* mousepad/mousepad-dbus-infos.xml, mousepad/mousepad-undo.c,
mousepad/mousepad-view.c: Fix some typos and replace tabs.
(Old svn revision: 25662)
commit 72d49ecc7196fe7c8d5479e978593718e3b17a39
Author: Nick Schermer <nick at xfce.org>
Date: Tue May 1 15:59:28 2007 +0000
* mousepad/mousepad-{document,window}.c: Allow uri and tab
drops inside the textview widget.
* mousepad/mousepad-window.c: Hide the searchbar when all the
tabs are closed and make sure nothing segfaults when no
active document is found.
* mousepad/mousepad-window.c: Make sure the textview is
focused when a new document has been added.
(Old svn revision: 25661)
commit 2beb0bf4ad103e2bc112b516f6253157e821e2a7
Author: Nick Schermer <nick at xfce.org>
Date: Sat Apr 28 18:59:18 2007 +0000
* TODO: Removed the items I've fixed.
* Changelog: Break after 70 characters.
* mousepad/mousepad-{document,statusbar}.c: Hide the visible
event box window.
* mousepad/mousepad-statusbar: The statusbar is now on top of
the Gtk statusbar.
* mousepad/mousepad-window.c: Add support for dropping uris
in the main window. Droping in the textview doesn't work
yet.
* mousepad/mousepad-window.c: Add tab dnd. This required some
function reordering, but in the end it was quite easy and
made the code more logical. I've also changed the window
behaviour: when you close all the tabs in a window, an empty
window is left. This is easier for tab dnd.
* mousepad/mousepad-{application,window}.c: Beter handing of
application windows. Also added detachable tabs.
* mousepad/mousepad-{properties,window}.c: The last settings
of line numbers and auto indent are now stored.
* mousepad/mousepad-window.c: Beter locking of menu updates,
this avoids some unneeded menu updates and also made the
start time of Mousepad equal to Leafpad (and probably also
Mosuepad 0.2.x), which is quite nice since it supports a
whole bunch of extra features.
* mousepad/mousepad-window.c: Removed the 'close all windows'
option from the menu and the code. Not need for a text
editor IMHO.
(Old svn revision: 25654)
commit a20de9eada02c7cc07cda472ea4f197c3e3b97aa
Author: Nick Schermer <nick at xfce.org>
Date: Sun Apr 22 08:23:48 2007 +0000
* TODO: Fix some typos and add some new ideas and reminders.
(Old svn revision: 25622)
commit 35f0b25ab3c544412ef32b71e4a8dd514142c281
Author: Nick Schermer <nick at xfce.org>
Date: Sat Apr 21 19:18:52 2007 +0000
* TODO: Add some stuff.
(Old svn revision: 25621)
commit 666a0aebba2bca3fbeee5a650fe9ffa160455348
Author: Nick Schermer <nick at xfce.org>
Date: Fri Apr 20 10:55:16 2007 +0000
* TODO, NEWS: Update and expand both documents.
* mousepad/mousepad-search-bar.c, mousepad-window.c: Fix some
compiler warnings.
(Old svn revision: 25615)
commit a47b71f49c5221348b83135ffbcf2a05abdebeb4
Author: Erik Harrison <erikharrison at gmail.com>
Date: Wed Apr 18 23:45:20 2007 +0000
Little compiler fix
(Old svn revision: 25600)
commit 44f28f94cfd8b18c76852726550c2fc476fb4801
Author: Nick Schermer <nick at xfce.org>
Date: Thu Apr 12 19:38:59 2007 +0000
* mousepad/mousepad-undo.c: Improve the behavior of the undo manager.
It now merges multiple spaces, new lines are a separate undo action and
when you redo some steps and then start editing again we append the redo-ed
steps in reversed order with an inverted action, so you can undo every thing
afterwards. This consumes a bit more memory compared to the old behavior
(which simply removed every redo-ed step), but it's much more consistent.
(Old svn revision: 25531)
commit 0210f46f4a9e604a52bda681c820e66d352303e5
Author: Nick Schermer <nick at xfce.org>
Date: Wed Apr 11 17:41:54 2007 +0000
* mousepad/mousepad-{window,view}.c: Fix some potential leaks, I'm not
really sure, but it doesn't hurt...
(Old svn revision: 25523)
commit d45ae8eaa022bd99c25c0db5e68594e12b25281d
Author: Nick Schermer <nick at xfce.org>
Date: Wed Apr 11 09:07:54 2007 +0000
* mousepad/mousepad-statusbar.c: Destroy the tool items together with the
searchbar (memory leak from valgrind).
(Old svn revision: 25516)
commit ec1234c58d36457576ea554ac2fd5885da962fd7
Author: Nick Schermer <nick at xfce.org>
Date: Wed Apr 11 08:22:46 2007 +0000
* mousepad/mousepad-{document,window,statusbar}: You can now click the OVR
text in the statusbar to toggle the overwrite mode.
* mousepad/mousepad-{document,window,undo}: Initial version of an undo
manager. This is not finished yet, but it's a start.
* mousepad/Makefile.am, mousepad/mousepad-csource.pl: Remove my perl script
because it has some problems. Use exo-csource instead.
* Remove some empty object functions.
(Old svn revision: 25514)
commit fe666120450ea8df11059d60c0bcdb806d3d5426
Author: Nick Schermer <nick at xfce.org>
Date: Fri Apr 6 19:06:59 2007 +0000
* mousepad/mousepad-document.c: Fix segfault from previous commit.
* mousepad/mousepad-document.c: Fix bug in searching backwards, we have to
jump one iter backwards before searching, because we start with the
character right from the first iter. Also removed the equal check because
it's not needed and only causing problems with backwards searching on the
first character in the buffer.
(Old svn revision: 25405)
commit 7b33a930bf6efc2c879dbb304e6ee0fd4adb2b5a
Author: Nick Schermer <nick at xfce.org>
Date: Fri Apr 6 16:36:12 2007 +0000
* mousepad/mousepad-window.c: Fix opening recent files that do not exist.
* mousepad/mousepad-window.c, mousepad/mousepad-document.c: Update the window
title correctly and remove the unused notify::title signals.
(Old svn revision: 25404)
commit 46e78d17d7e5fc5ae16f9e9ee6ae8103e683af53
Author: Nick Schermer <nick at xfce.org>
Date: Fri Apr 6 13:57:06 2007 +0000
* mousepad/mousepad-document.c: Because we don't use invisible characters in
the text buffer, I was able to write a custom iter search function that can
search in both directions, can be case insensitive, no string duplications
and above all: is over 10x faster then the gtk version. This gives a nice
performance boost to the highlight function.
* mousepad/mousepad-search-bar.c: Connected the last signals and added a
wrap around option to the search bar.
(Old svn revision: 25402)
commit 4ccd1ba30c425214156b07cf6e58b001fa8970d9
Author: Nick Schermer <nick at xfce.org>
Date: Thu Apr 5 11:34:07 2007 +0000
* mousepad/mousepad-document.c: Remove unused properties.
* mousepad/mousepad-{exo,window,preoperties,search-bar}: Remove the
exo bindings, mousepad only depends on Gtk and libxfce4util.
* mousepad/mousepad-csource.pl: Perl script to generate the menu code.
This script also strips the code between the xml blocks, so a minimal
amount of text is added to the binary (ie. it produces better code then
exo-csource).
(Old svn revision: 25385)
commit 2563fc285fb1b58ee9c7984b9a710b5668ef3c10
Author: Nick Schermer <nick at xfce.org>
Date: Wed Apr 4 18:42:18 2007 +0000
* po/: Update the POTFILES.in file and regenerate the pot file.
* mousepad/mousepad-document.c: Make the scroll offset smaller, so the
textview doesn't jump around while searching.
* mousepad/mousepad-search-bar.c: Make sure the search signal is send
when the search field is empty after a backspace, so no text is selected in
the buffer.
* Remove some leftover tabs.
* mousepad/mousepad-{search-bar,document,window).*: Added function for
* Various performance improvements and code cleanups.
(Old svn revision: 25382)
commit a0b89f341abe283462ad6f5c476b9d37ee644774
Author: Nick Schermer <nick at xfce.org>
Date: Wed Apr 4 15:50:00 2007 +0000
* configure.in.in: Mousepad does not depend on exo anymore. Only
when you run in maintainer-mode, exo-csource is required.
* mousepad/mousepad-document.*: Renamed mousepad-screen to mousepad-document.
* mousepad/mousepad-{search-bar,document,window).*: Added function for
searching, including the type-ahead search bar.
* mousepad/mousepad-statusbar.*: A custom statusbar suitable for displaying
the tooltips, overwrite/insert and the line- and column-number.
* mousepad/mousepad-exo.*: The exo-bindings so we don't depend on exo anymore,
although I want to get rid of all of them.
* mousepad/mousepad-marshal.list: Custom marshal for sending search signals.
* mousepad/mousepad-types.h: Search types (a mousepad-enum-types.{c,h} is
generated during build).
* Various performance inprovements and code cleanups.
(Old svn revision: 25379)
commit cab0d1a984369283d99376e364e216e7fff04bc3
Author: Nick Schermer <nick at xfce.org>
Date: Sat Mar 3 13:26:35 2007 +0000
* mousepad/mousepad-dialogs.{c,h}: Add response actions for Mousepad so it's
easier to understand what happens.
* mousepad/mousepad-window.c, * mousepad/mousepad-screen.{c,h}: Basic
support for document reloading.
(Old svn revision: 25072)
commit 8a1670c4888db706b49b21c32d78786bf9ed2780
Author: Nick Schermer <nick at xfce.org>
Date: Sat Mar 3 12:11:20 2007 +0000
* configure.in.in, mousepad/Makefile.am: Remove the PCRE dependency.
I should have removed this earier after some testing.
(Old svn revision: 25070)
commit b5178ab96dc4dd424f9ce884529a67e95731e145
Author: Nick Schermer <nick at xfce.org>
Date: Sat Mar 3 12:07:46 2007 +0000
* mousepad/mousepad-dialogs.{c,h}: New file for the dialogs
so the mousepad-window file becomes more readable.
* mousepad/mousepad-window.c: Split some function and reorder
them a bit.
* mousepad/mousepad-window.c: The items in the go menu now have
the filename as statusbar tooltip.
* mousepad/mousepad-window.c: Check if the file is externally modified
before writing. If it is, ask the user what to do.
* mousepad/main.c, mousepad/mousepad-dbus.{c,h},
mousepad/mousepad-dbus-infos.xml: Support for terminating a running
mousepad instance (that is connected to dbus), using mousepad -q.
* mousepad/*: Add function destriptions for Erik (and others) :-).
(Old svn revision: 25069)
commit 5263c479394dbb5a6bc0b892806f11a629bd81f4
Author: Nick Schermer <nick at xfce.org>
Date: Wed Feb 28 22:45:19 2007 +0000
* mousepad/mousepad-window.c: Improve the menu tooltips.
(Old svn revision: 25045)
commit 64c5d0349576fb0c55f7f9df7e985437c0fe8aea
Author: Nick Schermer <nick at xfce.org>
Date: Wed Feb 28 19:16:13 2007 +0000
* mousepad/mousepad-window.c: Prevent multiple recent- and go-menu
updates when multiple items are opened or the recent history is
cleared.
(Old svn revision: 25044)
commit f0dc6b97b9512e8121ad3b253562d13f5baca205
Author: Nick Schermer <nick at xfce.org>
Date: Wed Feb 28 18:14:25 2007 +0000
* TODO, mousepad/mousepad-window.c: Add option to the recent menu
to clear the history.
* mousepad/mousepad-window.c: The recent items are now in the Mousepad
group so it's easier to filter them when removing. The list is also
sorted by the most recently used item.
* mousepad/mousepad-window.c: Fix problem in checking if the
file was already openened.
* mousepad/mousepad-window.c: You can now reorder tabs.
* mousepad/mousepad-window.c: A tab is now inserted right from the
current active tab.
* mousepad/mousepad-screen.c: Untitled documents now have a number like
"Untitled #". The number is increased as long as the application is running.
* mousepad/mousepad-window.c: Added first parts of code for a statusbar.
(Old svn revision: 25043)
commit ff28ae1045a381c7dc815906b9a0816b87451e2b
Author: Nick Schermer <nick at xfce.org>
Date: Tue Feb 27 21:28:04 2007 +0000
* TODO: Remind myself to fix problems with UTF-8 filenames.
* mousepad/mousepad-window.c: Fix crash when opening files
with the command line.
* Mousepad.desktop.in.in, Mousepad.desktop.in.in: Mousepad executable
is now lowercase again.
* mousepad/mousepad-screen.c: Remove unused signals from the Class.
(Old svn revision: 25040)
commit 62dbff28f0cd78e96933a78c979ef3109a5a43ba
Author: Nick Schermer <nick at xfce.org>
Date: Tue Feb 27 17:20:46 2007 +0000
* TODO, NEWS: Update the current status of the rewrite.
* Mousepad.desktop.in.in: Add MimeType and improve Categories.
* Initial import of my Mousepad rewrite.
(Old svn revision: 25037)
commit 8a402ec2206b15649d4bc1a50621d3a52f11dd2b
Author: Nick Schermer <nick at xfce.org>
Date: Tue Feb 27 17:02:14 2007 +0000
* Import my Mousepad rewrite
(Old svn revision: 25036)
commit 1bd002d711d108435b12b0cf7aa694b908cd63ed
Author: Jean-François Wauthy <pollux at xfce.org>
Date: Tue Feb 27 16:53:45 2007 +0000
for nick
(Old svn revision: 25035)
.gitignore | 37 +
AUTHORS-pre-0.3.0 => AUTHORS | 3 +-
COPYING | 340 ++
INSTALL | 231 ++
Makefile.am | 63 +
Mousepad.desktop.in.in | 13 +
Mousepad.spec.in | 49 +
MousepadHelp.in | 95 +
NEWS | 42 +
README | 45 +
TODO | 43 +
autogen.sh | 15 +
configure.ac.in | 156 +
docs/Makefile.am | 4 +
docs/manual/C/Makefile.am | 66 +
docs/manual/C/Mousepad.xml.in | 478 +++
docs/manual/C/images/Makefile.am | 15 +
docs/manual/C/images/find-and-replace.png | Bin 0 -> 20777 bytes
docs/manual/C/images/go-to.png | Bin 0 -> 9507 bytes
docs/manual/C/images/main-window.png | Bin 0 -> 13955 bytes
docs/manual/C/images/other-tab-size.png | Bin 0 -> 8168 bytes
docs/manual/C/images/paste-from-history.png | Bin 0 -> 9495 bytes
docs/manual/C/images/tab-size-menu.png | Bin 0 -> 9130 bytes
docs/manual/C/images/typeahead-search.png | Bin 0 -> 6724 bytes
docs/manual/Makefile.am | 12 +
docs/manual/mousepad.css | 99 +
docs/manual/mousepad.xsl | 254 ++
icons/16x16/Makefile.am | 13 +
icons/16x16/Mousepad.png | Bin 0 -> 574 bytes
icons/24x24/Makefile.am | 13 +
icons/24x24/Mousepad.png | Bin 0 -> 972 bytes
icons/Makefile.am | 19 +
icons/scalable/Makefile.am | 13 +
icons/scalable/Mousepad.svg | 552 +++
mousepad/Makefile.am | 118 +
mousepad/main.c | 202 ++
mousepad/mousepad-application.c | 299 ++
mousepad/mousepad-application.h | 48 +
mousepad/mousepad-dbus-infos.xml | 55 +
mousepad/mousepad-dbus.c | 337 ++
mousepad/mousepad-dbus.h | 40 +
mousepad/mousepad-dialogs.c | 453 +++
mousepad/mousepad-dialogs.h | 69 +
mousepad/mousepad-document.c | 649 ++++
mousepad/mousepad-document.h | 86 +
mousepad/mousepad-encoding-dialog.c | 517 +++
mousepad/mousepad-encoding-dialog.h | 45 +
mousepad/mousepad-encoding.c | 201 ++
mousepad/mousepad-encoding.h | 125 +
mousepad/mousepad-file.c | 919 +++++
mousepad/mousepad-file.h | 97 +
mousepad/mousepad-marshal.list | 3 +
mousepad/mousepad-preferences.c | 652 ++++
mousepad/mousepad-preferences.h | 38 +
mousepad/mousepad-print.c | 805 +++++
mousepad/mousepad-print.h | 43 +
mousepad/mousepad-private.h | 126 +
mousepad/mousepad-replace-dialog.c | 653 ++++
mousepad/mousepad-replace-dialog.h | 42 +
mousepad/mousepad-search-bar.c | 536 +++
mousepad/mousepad-search-bar.h | 46 +
mousepad/mousepad-statusbar.c | 300 ++
mousepad/mousepad-statusbar.h | 49 +
mousepad/mousepad-util.c | 1206 +++++++
mousepad/mousepad-util.h | 138 +
mousepad/mousepad-view.c | 2402 +++++++++++++
mousepad/mousepad-view.h | 121 +
mousepad/mousepad-window-ui.xml | 159 +
mousepad/mousepad-window.c | 5102 +++++++++++++++++++++++++++
mousepad/mousepad-window.h | 59 +
po/POTFILES.in | 27 +
71 files changed, 19436 insertions(+), 1 deletions(-)
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..3e3e61d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,37 @@
+*.o
+Makefile
+Makefile.in
+Makefile.in.in
+Mousepad.desktop
+MousepadHelp
+aclocal.m4
+autom4te.cache/
+compile
+config.guess
+config.h
+config.h.in
+config.log
+config.status
+config.sub
+configure
+configure.ac
+depcomp
+docs/manual/C/Mousepad.xml
+install-sh
+intltool-extract.in
+intltool-merge.in
+intltool-update.in
+libtool
+ltmain.sh
+missing
+mkinstalldirs
+mousepad/.deps/
+mousepad/mousepad
+mousepad/mousepad-dbus-infos.h
+mousepad/mousepad-marshal.c
+mousepad/mousepad-marshal.h
+mousepad/mousepad-window-ui.h
+po/.intltool-merge-cache
+po/POTFILES
+po/stamp-it
+stamp-h1
diff --git a/AUTHORS-pre-0.3.0 b/AUTHORS
similarity index 54%
copy from AUTHORS-pre-0.3.0
copy to AUTHORS
index 1c0188e..fefc28d 100644
--- a/AUTHORS-pre-0.3.0
+++ b/AUTHORS
@@ -1,3 +1,4 @@
Erik Harrison <erikharrison at xfce.org>
+Nick Schermer <nick at xfce.org>
Benedikt Meurer <benny at xfce.org>
-Tarot Osuji <tarot at sdf.lonestar.org>
+Matthew Brush <matt at xfce.org>
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..d60c31a
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..095b1eb
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,231 @@
+Installation Instructions
+*************************
+
+Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004 Free
+Software Foundation, Inc.
+
+This file is free documentation; the Free Software Foundation gives
+unlimited permission to copy, distribute and modify it.
+
+Basic Installation
+==================
+
+These are generic installation instructions.
+
+ The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation. It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions. Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, and a
+file `config.log' containing compiler output (useful mainly for
+debugging `configure').
+
+ It can also use an optional file (typically called `config.cache'
+and enabled with `--cache-file=config.cache' or simply `-C') that saves
+the results of its tests to speed up reconfiguring. (Caching is
+disabled by default to prevent problems with accidental use of stale
+cache files.)
+
+ If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release. If you are using the cache, and at
+some point `config.cache' contains results you don't want to keep, you
+may remove or edit it.
+
+ The file `configure.ac' (or `configure.in') is used to create
+`configure' by a program called `autoconf'. You only need
+`configure.ac' if you want to change it or regenerate `configure' using
+a newer version of `autoconf'.
+
+The simplest way to compile this package is:
+
+ 1. `cd' to the directory containing the package's source code and type
+ `./configure' to configure the package for your system. If you're
+ using `csh' on an old version of System V, you might need to type
+ `sh ./configure' instead to prevent `csh' from trying to execute
+ `configure' itself.
+
+ Running `configure' takes awhile. While running, it prints some
+ messages telling which features it is checking for.
+
+ 2. Type `make' to compile the package.
+
+ 3. Optionally, type `make check' to run any self-tests that come with
+ the package.
+
+ 4. Type `make install' to install the programs and any data files and
+ documentation.
+
+ 5. You can remove the program binaries and object files from the
+ source code directory by typing `make clean'. To also remove the
+ files that `configure' created (so you can compile the package for
+ a different kind of computer), type `make distclean'. There is
+ also a `make maintainer-clean' target, but that is intended mainly
+ for the package's developers. If you use it, you may have to get
+ all sorts of other programs in order to regenerate files that came
+ with the distribution.
+
+Compilers and Options
+=====================
+
+Some systems require unusual options for compilation or linking that the
+`configure' script does not know about. Run `./configure --help' for
+details on some of the pertinent environment variables.
+
+ You can give `configure' initial values for configuration parameters
+by setting variables in the command line or in the environment. Here
+is an example:
+
+ ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix
+
+ *Note Defining Variables::, for more details.
+
+Compiling For Multiple Architectures
+====================================
+
+You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory. To do this, you must use a version of `make' that
+supports the `VPATH' variable, such as GNU `make'. `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script. `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+ If you have to use a `make' that does not support the `VPATH'
+variable, you have to compile the package for one architecture at a
+time in the source code directory. After you have installed the
+package for one architecture, use `make distclean' before reconfiguring
+for another architecture.
+
+Installation Names
+==================
+
+By default, `make install' will install the package's files in
+`/usr/local/bin', `/usr/local/man', etc. You can specify an
+installation prefix other than `/usr/local' by giving `configure' the
+option `--prefix=PREFIX'.
+
+ You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files. If you
+give `configure' the option `--exec-prefix=PREFIX', the package will
+use PREFIX as the prefix for installing programs and libraries.
+Documentation and other data files will still use the regular prefix.
+
+ In addition, if you use an unusual directory layout you can give
+options like `--bindir=DIR' to specify different values for particular
+kinds of files. Run `configure --help' for a list of the directories
+you can set and what kinds of files go in them.
+
+ If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System). The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+ For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+There may be some features `configure' cannot figure out automatically,
+but needs to determine by the type of machine the package will run on.
+Usually, assuming the package is built to be run on the _same_
+architectures, `configure' can figure that out, but if it prints a
+message saying it cannot guess the machine type, give it the
+`--build=TYPE' option. TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name which has the form:
+
+ CPU-COMPANY-SYSTEM
+
+where SYSTEM can have one of these forms:
+
+ OS KERNEL-OS
+
+ See the file `config.sub' for the possible values of each field. If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the machine type.
+
+ If you are _building_ compiler tools for cross-compiling, you should
+use the `--target=TYPE' option to select the type of system they will
+produce code for.
+
+ If you want to _use_ a cross compiler, that generates code for a
+platform different from the build platform, you should specify the
+"host" platform (i.e., that on which the generated programs will
+eventually be run) with `--host=TYPE'.
+
+Sharing Defaults
+================
+
+If you want to set default values for `configure' scripts to share, you
+can create a site shell script called `config.site' that gives default
+values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists. Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Defining Variables
+==================
+
+Variables not defined in a site shell script can be set in the
+environment passed to `configure'. However, some packages may run
+configure again during the build, and the customized values of these
+variables may be lost. In order to avoid this problem, you should set
+them in the `configure' command line, using `VAR=value'. For example:
+
+ ./configure CC=/usr/local2/bin/gcc
+
+will cause the specified gcc to be used as the C compiler (unless it is
+overridden in the site shell script).
+
+`configure' Invocation
+======================
+
+`configure' recognizes the following options to control how it operates.
+
+`--help'
+`-h'
+ Print a summary of the options to `configure', and exit.
+
+`--version'
+`-V'
+ Print the version of Autoconf used to generate the `configure'
+ script, and exit.
+
+`--cache-file=FILE'
+ Enable the cache: use and save the results of the tests in FILE,
+ traditionally `config.cache'. FILE defaults to `/dev/null' to
+ disable caching.
+
+`--config-cache'
+`-C'
+ Alias for `--cache-file=config.cache'.
+
+`--quiet'
+`--silent'
+`-q'
+ Do not print messages saying which checks are being made. To
+ suppress all normal output, redirect it to `/dev/null' (any error
+ messages will still be shown).
+
+`--srcdir=DIR'
+ Look for the package's source code in directory DIR. Usually
+ `configure' can determine that directory automatically.
+
+`configure' also accepts some other, not widely useful, options. Run
+`configure --help' for more details.
+
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..25a1d67
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,63 @@
+SUBDIRS = \
+ icons \
+ mousepad \
+ po \
+ docs
+
+distclean-local:
+ rm -rf *.spec *.cache *~
+
+rpm: dist
+ rpmbuild -ta $(PACKAGE)-$(VERSION).tar.gz
+ @rm -f $(PACKAGE)-$(VERSION).tar.gz
+
+mousepad_scriptsdir = $(libdir)/xfce4/mousepad
+mousepad_scripts_SCRIPTS = \
+ MousepadHelp
+
+MousepadHelp: MousepadHelp.in Makefile
+ rm -f MousepadHelp.gen MousepadHelp
+ sed -e "s,\@datadir\@,$(datadir),g" \
+ < $(srcdir)/MousepadHelp.in \
+ > MousepadHelp.gen
+ mv MousepadHelp.gen MousepadHelp
+
+desktopdir = $(datadir)/applications
+desktop_in_in_files = \
+ Mousepad.desktop.in.in
+desktop_in_files = $(desktop_in_in_files:.desktop.in.in=.desktop.in)
+%.desktop.in: %.desktop.in.in
+ sed -e "s,\@libexecdir\@,$(libexecdir),g" < $< > $@
+desktop_DATA = $(desktop_in_files:.desktop.in=.desktop)
+ at INTLTOOL_DESKTOP_RULE@
+
+.PHONY: ChangeLog
+
+ChangeLog: Makefile
+ (GIT_DIR=$(top_srcdir)/.git git log > .changelog.tmp \
+ && mv .changelog.tmp ChangeLog; rm -f .changelog.tmp) \
+ || (touch ChangeLog; echo 'Git directory not found: installing possibly empty changelog.' >&2)
+
+dist-hook: ChangeLog
+
+EXTRA_DIST = \
+ MousepadHelp.in \
+ Mousepad.spec.in \
+ intltool-extract.in \
+ intltool-merge.in \
+ intltool-update.in \
+ $(desktop_in_in_files)
+
+DISTCLEANFILES = \
+ MousepadHelp \
+ Mousepad.spec \
+ intltool-extract \
+ intltool-merge \
+ intltool-update \
+ $(desktop_in_files) \
+ $(desktop_DATA)
+
+DISTCHECK_CONFIGURE_FLAGS = \
+ --enable-xsltproc
+
+# vi:set ts=8 sw=8 noet ai nocindent syntax=automake:
diff --git a/Mousepad.desktop.in.in b/Mousepad.desktop.in.in
new file mode 100644
index 0000000..21fda29
--- /dev/null
+++ b/Mousepad.desktop.in.in
@@ -0,0 +1,13 @@
+
+[Desktop Entry]
+Encoding=UTF-8
+_Name=Mousepad
+_Comment=Simple Text Editor
+_GenericName=Text Editor
+Exec=mousepad %F
+Icon=accessories-text-editor
+Terminal=false
+StartupNotify=true
+Type=Application
+Categories=Application;Utility;TextEditor;GTK;
+MimeType=text/plain
diff --git a/Mousepad.spec.in b/Mousepad.spec.in
new file mode 100644
index 0000000..6261c2c
--- /dev/null
+++ b/Mousepad.spec.in
@@ -0,0 +1,49 @@
+Summary: Mousepad Text Editor
+Name: @PACKAGE_TARNAME@
+Version: @PACKAGE_VERSION@
+Release: 1
+License: GPL
+URL: http://www.xfce.org/
+Source0: %{name}-%{version}.tar.gz
+Group: Applications/X11
+BuildRoot: %{_tmppath}/%{name}-root
+Requires: gtk2 >= @GTK_REQUIRED_VERSION@
+BuildRequires: gtk2-devel >= @GTK_REQUIRED_VERSION@
+
+%description
+Mousepad is a simple text editor for the Xfce Desktop Environment.
+
+%prep
+%setup -q
+
+%build
+%configure --enable-dbus --enable-final --enable-xsltproc
+make
+
+%install
+rm -rf $RPM_BUILD_ROOT
+make install DESTDIR=$RPM_BUILD_ROOT mandir=%{_mandir}
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%post
+update-desktop-database &> /dev/null ||:
+touch --no-create %{_datadir}/icons/hicolor || :
+if [ -x %{_bindir}/gtk-update-icon-cache ]; then
+ %{_bindir}/gtk-update-icon-cache --quiet %{_datadir}/icons/hicolor || :
+fi
+
+%postun
+update-desktop-database &> /dev/null ||:
+touch --no-create %{_datadir}/icons/hicolor || :
+if [ -x %{_bindir}/gtk-update-icon-cache ]; then
+ %{_bindir}/gtk-update-icon-cache --quiet %{_datadir}/icons/hicolor || :
+fi
+
+%files
+%defattr(-,root,root)
+%doc AUTHORS ChangeLog COPYING INSTALL NEWS README THANKS TODO
+%{_bindir}/
+%{_datadir}/
+%{_libexecdir}/
diff --git a/MousepadHelp.in b/MousepadHelp.in
new file mode 100644
index 0000000..bde651f
--- /dev/null
+++ b/MousepadHelp.in
@@ -0,0 +1,95 @@
+#!/bin/sh
+#
+# Copyright (c) 2004-2006 Benedikt Meurer <benny at xfce.org>
+# Copyright (c) 2007 Nick Schermer <nick at xfce.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+
+HELPDIR="@datadir@/doc/Mousepad/html"
+APPLICATIONS="exo-open firefox epiphany opera galeon mozilla konqueror dillo"
+
+# try to find a language
+if test -n "$LC_ALL"; then
+ LC=$LC_ALL
+elif test -n "$LANG"; then
+ LC=$LANG
+else
+ LC="C"
+fi
+
+# set the document or use index.html
+if test -n "$1"; then
+ HELPFILE="$1.html"
+else
+ HELPFILE="index.html"
+fi
+
+# test if the file exists, fallback on the C language or the C/index.html file
+if test -r "$HELPDIR/$LC/$HELPFILE"; then
+ URL="file://$HELPDIR/$LC/$HELPFILE"
+elif test -r "$HELPDIR/`echo $LC | sed 's/\(..\)_.*/\1/'`/$HELPFILE"; then
+ URL="file://$HELPDIR/`echo $LC | sed 's/\(..\)_.*/\1/'`/$HELPFILE"
+else
+ URL="file://$HELPDIR/C/index.html"
+fi
+
+# jump to a section if provided
+[ -n "$2" ] && URL="$URL#$2"
+
+# find a suitable browser to launch if no BROWSER variable is set
+if [ "x$BROWSER" = "x" ]; then
+ for i in $APPLICATIONS; do
+ # find the application in the path
+ testapp=$(which $i 2>/dev/null)
+ if test -f "$testapp"; then
+ # use the application and break
+ BROWSER=$i
+ break
+ fi
+ done
+fi
+
+# tell the use if no suitable browser was found
+if [ "x$BROWSER" = "x" ]; then
+ # print warning and leave
+ echo "MousepadHelp: Could not find a browser to use. Please set the BROWSER variable."
+ exit 1
+fi
+
+# run the browser
+case $BROWSER in
+ exo-open)
+ $BROWSER --launch WebBrowser $URL
+ ;;
+ opera*)
+ $BROWSER -remote openURL\($URL,new-window\) || $BROWSER $URL
+ ;;
+ firefox*)
+ $BROWSER -a firefox -remote openurl\($URL,new-window\) || $BROWSER $URL
+ ;;
+ communicator*|netscape|mozilla*|phoenix*|firebird*)
+ $BROWSER -remote openurl\($URL,new-window\) || $BROWSER $URL
+ ;;
+ *)
+ $BROWSER $URL;
+ ;;
+esac
+
+# leave
+exit 0
+
+# vim:set ts=2 sw=2 et ai:
+
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..b1dc310
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,42 @@
+0.3.0
+=====
+Highlights:
+ - A complete rewrite of Mousepad. The code is now written in GOjects
+ where possible so we have a solid base for further development.
+ - Run multiple windows in one instance. We also invoke a running
+ instance with DBus (optional), this is needed for tab DND between
+ windows.
+ - Drag and Drop support for text inside a document, between tabs and
+ between windows. Also supports opening a file by dragging it into
+ the window.
+ - Support for multiple tabs in one window which are both reorderable
+ and detachable.
+ - Load multiple files at once from both the open dialog and the command
+ line.
+ - Type-ahead find and highlight feature like Firefox.
+ - Support for editable menu accelerators.
+ - Reimplemented recent file support. You can cleanup the Mousepad history
+ and a file is removed from the history when Mousepad was unable to open
+ it.
+ - Syntax highlighting of many filetypes (including printing).
+ - Various hidden settings.
+ - Statusbar with filetype, cursor location and whether overwrite is
+ actived.
+ - Mousepad depends on GTK+ 2.20.
+
+Bug fixes:
+ - Save button is now default response in the dialog (Bug #2941).
+ - No font is set as long the user has not defined any (Bug #2720).
+ - No compiler warnings (Bug #1697).
+ - Windows geometry is now properly saved (Bug #2945).
+ - Hidden option to show the full path in the window title (Bug #2896).
+ - Basic command line options (Bug #2397).
+ - The MousepadPreferences object uses XfceRc for saving the
+ settings (Bug #2786).
+ - No segfault when there is no rc file (Bug #2784).
+ - Support syntax highlighting using GtkSourceView (Bug #3228 & #5803).
+ - Proper Drag and Drop support (Bug #2055).
+ - Fix recent files support (Bug #5635, #3850 & #7890).
+ - Use GTK+ printing (Bug #4477).
+ - Settings saved in Find (and Replace) dialogs (Bug #7246).
+ - No deprecated dependencies (Bug #7259).
diff --git a/README b/README
new file mode 100644
index 0000000..0c53e33
--- /dev/null
+++ b/README
@@ -0,0 +1,45 @@
+What is it?
+===========
+
+Mousepad is a simple text editor for the Xfce desktop environment.
+
+
+The Mousepad Mission
+====================
+
+Mousepad aims to be an easy-to-use and fast editor. Our target is an
+editor for quickly editing text files, not a development environment
+or an editor with a huge bunch of plugins.
+On the other hand we try to use the latest Gtk+ features available,
+which means that if Gtk adds something new in a major release that is
+useful for the editor, we will likely bump the Gtk dependency and
+integrate this new feature in Mousepad.
+
+
+Required packages
+=================
+
+Mousepad depends on the following packages:
+
+ - Gtk+ 2.20.0 or above
+ - Libxfce4util 4.4.0 or above
+ - GtkSourceView 2.2.2 or above
+
+The following packages are optional:
+
+ - D-BUS 0.34 or above (strongly suggested)
+
+
+Installation
+============
+
+The file 'INSTALL' contains generic installation
+instructions.
+
+
+How to report bugs?
+===================
+
+Bugs should be reported to the Xfce bug tracking system
+(http://bugzilla.xfce.org, product Mousepad). You will need to create
+an account for yourself.
diff --git a/THANKS b/THANKS
new file mode 100644
index 0000000..e69de29
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..6976a23
--- /dev/null
+++ b/TODO
@@ -0,0 +1,43 @@
+All the items below, except those under the feature heading, must
+be fixed (or ignored when irrelevant) before the nick_0_3 branch
+can move to trunk. When the branch enters trunk, it's important
+we work towards a stable version, so we can release a 0.2.90.1
+(0.3 beta 1) version as soon as possible (if needed, independent
+from the Xfce release schedule).
+
+
+Interface
+=========
+- Test the application with a screen reader and add Atk objects
+ where needed.
+- Check the search and replace code for bugs.
+- Add option to disable the search feedback in the replace dialog
+ (highlight, count matches, type-ahead). This can slow down mousepad
+ with (multiple) large documents.
+- Transpose words works a bit odd sometimes.
+- Improve replace (all) in selection.
+
+
+Saving and loading
+==================
+- Cleanup and improve encoding support and order the encoding types.
+
+
+Other
+=====
+- We need a cool Mousepad logo.
+- Write documentation.
+- Session manager support (waiting for Gtk+ here).
+
+
+Testing and polishing
+=====================
+- Profiling loading large files (Gtk/Pango has a problem here). This
+ also locks some dialogs.
+- Checking for memory leaks.
+
+
+Feature
+=======
+- You'll never know. No promisses here...
+- GIO.
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 0000000..fb9b80b
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+(type xdt-autogen) >/dev/null 2>&1 || {
+ cat >&2 <<EOF
+autogen.sh: You don't seem to have the Xfce development tools installed on
+ your system, which are required to build this software.
+ Please install the xfce4-dev-tools package first, it is available
+ from http://www.xfce.org/.
+EOF
+ exit 1
+}
+
+XDT_AUTOGEN_REQUIRED_VERSION="4.7.2" exec xdt-autogen $@
+
+# vi:set ts=2 sw=2 et ai:
diff --git a/configure.ac.in b/configure.ac.in
new file mode 100644
index 0000000..9c820c6
--- /dev/null
+++ b/configure.ac.in
@@ -0,0 +1,156 @@
+dnl ***************************
+dnl *** Version information ***
+dnl ***************************
+m4_define([mousepad_version_major], [0])
+m4_define([mousepad_version_minor], [3])
+m4_define([mousepad_version_micro], [0])
+m4_define([mousepad_version_nano], []) dnl leave this empty to have no nano version
+m4_define([mousepad_version_build], [@REVISION@])
+m4_define([mousepad_version_tag], [git])
+m4_define([mousepad_version], [mousepad_version_major().mousepad_version_minor().mousepad_version_micro()ifelse(mousepad_version_nano(), [], [], [.mousepad_version_nano()])ifelse(mousepad_version_tag(), [git], [mousepad_version_tag()-mousepad_version_build()], [mousepad_version_tag()])])
+
+dnl *******************************************
+dnl *** Debugging support for GIT snapshots ***
+dnl *******************************************
+m4_define([mousepad_debug_default], [ifelse(mousepad_version_tag(), [git], [yes], [minimum])])
+
+dnl ***************************
+dnl *** Initialize autoconf ***
+dnl ***************************
+AC_COPYRIGHT([Copyright (c) 2007-2010
+ The Xfce development team. All rights reserved.])
+AC_INIT([Mousepad], [mousepad_version], [http://bugzilla.xfce.org/], [mousepad])
+AC_PREREQ([2.50])
+AC_CANONICAL_TARGET()
+AC_REVISION([])
+
+dnl ***************************
+dnl *** Initialize automake ***
+dnl ***************************
+AM_INIT_AUTOMAKE([1.8 dist-bzip2 tar-ustar])
+AM_CONFIG_HEADER([config.h])
+AM_MAINTAINER_MODE()
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+
+dnl *******************************
+dnl *** Check for UNIX variants ***
+dnl *******************************
+AC_AIX()
+AC_ISC_POSIX()
+AC_MINIX()
+
+dnl ********************************
+dnl *** Check for basic programs ***
+dnl ********************************
+AC_PROG_CC()
+AM_PROG_CC_C_O()
+AC_PROG_LD()
+AC_PROG_INSTALL()
+AC_PROG_INTLTOOL()
+AC_CHECK_PROGS([PERL], [perl5 perl])
+
+dnl **************************
+dnl *** Initialize libtool ***
+dnl **************************
+AC_PROG_LIBTOOL()
+
+dnl **************************************
+dnl *** Substitute version information ***
+dnl **************************************
+MOUSEPAD_VERSION_MAJOR=mousepad_version_major()
+MOUSEPAD_VERSION_MINOR=mousepad_version_minor()
+MOUSEPAD_VERSION_MICRO=mousepad_version_micro()
+MOUSEPAD_VERSION_NANO=mousepad_version_nano()
+AC_SUBST([MOUSEPAD_VERSION_MAJOR])
+AC_SUBST([MOUSEPAD_VERSION_MINOR])
+AC_SUBST([MOUSEPAD_VERSION_MICRO])
+AC_SUBST([MOUSEPAD_VERSION_NANO])
+
+dnl **********************************
+dnl *** Check for standard headers ***
+dnl **********************************
+AC_CHECK_HEADERS([errno.h fcntl.h libintl.h memory.h math.h stdlib.h \
+ string.h sys/types.h sys/stat.h time.h unistd.h])
+
+dnl ******************************
+dnl *** Check for i18n support ***
+dnl ******************************
+XDT_I18N([@LINGUAS@])
+
+dnl ***********************************
+dnl *** Check for required packages ***
+dnl ***********************************
+XDT_CHECK_PACKAGE([GLIB], [glib-2.0], [2.12.0])
+XDT_CHECK_PACKAGE([GTHREAD], [gthread-2.0], [2.12.0])
+XDT_CHECK_PACKAGE([GTK], [gtk+-2.0], [2.10.0])
+XDT_CHECK_PACKAGE([GTKSOURCEVIEW], [gtksourceview-2.0])
+
+dnl **********************************
+dnl *** Optional support for D-BUS ***
+dnl **********************************
+XDT_CHECK_OPTIONAL_PACKAGE([DBUS], [dbus-glib-1],
+ [0.34], [dbus], [D-BUS support])
+
+dnl **************************
+dnl *** Check for xsltproc ***
+dnl **************************
+AC_ARG_ENABLE([xsltproc], [AC_HELP_STRING([--enable-xsltproc], [Use xsltproc to build documentation @<:@default=no@:>@])],, [enable_xsltproc=no])
+if test x"$enable_xsltproc" = x"yes"; then
+ AC_PATH_PROG([XSLTPROC], [xsltproc], [no])
+ if test x"$XSLTPROC" = x"no"; then
+ enable_xsltproc=no
+ fi
+fi
+AM_CONDITIONAL([ENABLE_XSLTPROC], [test x"$enable_xsltproc" = x"yes"])
+
+dnl ***********************************
+dnl *** Check for debugging support ***
+dnl ***********************************
+XDT_FEATURE_DEBUG([mousepad_debug_default])
+
+dnl **************************************
+dnl *** Check for linker optimizations ***
+dnl **************************************
+XDT_FEATURE_LINKER_OPTS()
+
+dnl *********************************
+dnl *** Substitute platform flags ***
+dnl *********************************
+AC_MSG_CHECKING([PLATFORM_CPPFLAGS])
+AC_MSG_RESULT([$PLATFORM_CPPFLAGS])
+AC_SUBST([PLATFORM_CPPFLAGS])
+AC_MSG_CHECKING([PLATFORM_CFLAGS])
+AC_MSG_RESULT([$PLATFORM_CFLAGS])
+AC_SUBST([PLATFORM_CFLAGS])
+AC_MSG_CHECKING([PLATFORM_LDFLAGS])
+AC_MSG_RESULT([$PLATFORM_LDFLAGS])
+AC_SUBST([PLATFORM_LDFLAGS])
+
+AC_OUTPUT([
+Makefile
+docs/Makefile
+docs/manual/Makefile
+docs/manual/C/Makefile
+docs/manual/C/Mousepad.xml
+docs/manual/C/images/Makefile
+icons/Makefile
+icons/16x16/Makefile
+icons/24x24/Makefile
+icons/scalable/Makefile
+mousepad/Makefile
+po/Makefile.in
+])
+
+dnl ***************************
+dnl *** Print configuration ***
+dnl ***************************
+echo
+echo "Build Configuration:"
+echo
+if test x"$DBUS_FOUND" = x"yes"; then
+echo "* D-BUS support: yes"
+else
+echo "* D-BUS support: no"
+fi
+echo "* Debug Support: $enable_debug"
+echo
diff --git a/docs/Makefile.am b/docs/Makefile.am
new file mode 100644
index 0000000..7511289
--- /dev/null
+++ b/docs/Makefile.am
@@ -0,0 +1,4 @@
+SUBDIRS = \
+ manual
+
+# vi:set ts=8 sw=8 noet ai nocindent syntax=automake:
diff --git a/docs/manual/C/Makefile.am b/docs/manual/C/Makefile.am
new file mode 100644
index 0000000..06db614
--- /dev/null
+++ b/docs/manual/C/Makefile.am
@@ -0,0 +1,66 @@
+SUBDIRS = \
+ images
+
+TARGET_DIR = $(datadir)/doc/Mousepad/html/C
+STYLESHEET = ../mousepad.xsl
+DOCUMENT = Mousepad.xml
+
+# We set GPATH here; this gives us semantics for GNU make
+# which are more like other make's VPATH, when it comes to
+# whether a source that is a target of one rule is then
+# searched for in VPATH/GPATH.
+GPATH = $(srcdir)
+
+DOC_STAMPS = html-build.stamp
+
+EXTRA_DIST = $(DOCUMENT)
+CLEANFILES = $(DOC_STAMPS)
+
+if ENABLE_XSLTPROC
+all-local: html-build.stamp
+
+html-build.stamp: $(srcdir)/$(DOCUMENT) $(srcdir)/$(STYLESHEET)
+ @echo "*** Building HTML ***"
+ @-chmod -R u+w $(srcdir)
+ rm -rf $(srcdir)/html
+ mkdir $(srcdir)/html
+ $(XSLTPROC) --nonet -o $(srcdir)/html/ $(srcdir)/$(STYLESHEET) \
+ $(srcdir)/$(DOCUMENT)
+ touch html-build.stamp
+else
+all-local:
+endif
+
+maintainer-clean-local: clean
+ (cd $(srcdir) && rm -rf html)
+
+install-data-local:
+ installfiles=`echo $(srcdir)/html/*`; \
+ if test "$$installfiles" = '$(srcdir)/html/*'; then \
+ echo "--- Nothing to install"; \
+ else \
+ $(mkinstalldirs) $(DESTDIR)$(TARGET_DIR); \
+ for file in $$installfiles; do \
+ echo "--- Installing "$$file; \
+ $(INSTALL_DATA) $$file $(DESTDIR)$(TARGET_DIR); \
+ done; \
+ fi
+
+uninstall-local:
+ rm -rf $(DESTDIR)$(TARGET_DIR)/*
+
+if ENABLE_XSLTPROC
+dist-check-xsltproc: all
+else
+dist-check-xsltproc:
+ @echo "*** xsltproc must be installed and enabled in order to make dist"
+ @false
+endif
+
+dist-hook: dist-check-xsltproc dist-hook-local
+ mkdir $(distdir)/html
+ -cp $(srcdir)/html/* $(distdir)/html
+
+.PHONY: dist-hook-local
+
+# vi:set ts=8 sw=8 noet ai nocindent syntax=automake:
diff --git a/docs/manual/C/Mousepad.xml.in b/docs/manual/C/Mousepad.xml.in
new file mode 100644
index 0000000..fcd6ce3
--- /dev/null
+++ b/docs/manual/C/Mousepad.xml.in
@@ -0,0 +1,478 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
+ <!ENTITY date "OCtober 2007">
+ <!ENTITY version "@PACKAGE_VERSION@">
+ <!ENTITY application "@PACKAGE_NAME@">
+]>
+<article id="index" lang="en">
+
+ <articleinfo>
+ <title>Mousepad Text Editor</title>
+
+ <pubdate>&date;</pubdate>
+
+ <copyright>
+ <year>2007</year>
+ <holder>Nick Schermer</holder>
+ </copyright>
+
+ <legalnotice id="legalnotice">
+ <para>
+ Permission is granted to copy, distribute and/or modify this document
+ under the terms of the GNU Free Documentation License, Version 1.1 or
+ any later version published by the Free Software Foundation; with no
+ Invariant Sections, with no Front-Cover Texts, and with no Back-Cover
+ Texts. The complete license text is available from the <ulink
+ type="http" url="http://www.gnu.org/">Free Software Foundation</ulink>.
+ </para>
+ </legalnotice>
+
+ <authorgroup>
+ <author>
+ <firstname>Nick</firstname>
+ <surname>Schermer</surname>
+ <affiliation>
+ <orgname>Xfce Development Team</orgname>
+ <address><email>nick at xfce.org</email></address>
+ </affiliation>
+ </author>
+ </authorgroup>
+
+ <releaseinfo>
+ This manual describes version &version; of &application;.
+ </releaseinfo>
+ </articleinfo>
+
+ <sect1 id="preface">
+ <title>Preface</title>
+
+ <sect2 id="introduction">
+ <title>Introduction</title>
+
+ <para>
+ &application; is the default text editor for the Xfce Desktop Environment. It has
+ been design to be a lightweight editor you would use for basic file editing.
+ Therefore &application; starts quickly, but also has features like editing
+ multiple documents, vertical selection, type-ahead search, full tab drag and drop
+ and much more.
+ </para>
+
+ <para>
+ &application; started as a fork of Leafpad to provide printing support using
+ Xfprint, but as of version 0.3 is has been completely rewritten to add support for
+ tabs and DBus. Although the rewrite added a lot of new features, it is still as
+ fast as the Leafpad based fork and therefore fits perfectly in the Xfce philosophy.
+ </para>
+ </sect2>
+
+ <sect2 id="terminology">
+ <title>Terminology</title>
+
+ <para>
+ There might be a couple of words in this manual that could be the source of
+ misunderstanding. The table below will clarify those words.
+ </para>
+
+ <table frame="all">
+ <title>Terminology</title>
+ <tgroup cols='2'>
+ <thead>
+ <row>
+ <entry>Term</entry>
+ <entry>Explanation</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>Caret</entry>
+ <entry>The text insertion point.</entry>
+ </row>
+ <row>
+ <entry>Cursor</entry>
+ <entry>Mouse pointer.</entry>
+ </row>
+ <row>
+ <entry>Document</entry>
+ <entry>A file loaded in &application;.</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </sect2>
+ </sect1>
+
+ <sect1 id="working-with-text">
+ <title>Working With Text</title>
+
+ <sect2 id="keyboard-shortcuts">
+ <title>Keyboard Shortcuts</title>
+ </sect2>
+
+ <sect2 id="undo-and-redo">
+ <title>Undo and Redo</title>
+
+ <para>
+
+ </para>
+ </sect2>
+
+ <sect2 id="copy-and-paste">
+ <title>Copy and Paste</title>
+
+ <para>
+ &application; supports both the selection and primary clipboard. To paste
+ something from the seletion clipboard you can click with the middle mouse
+ button in the &application; window.
+ </para>
+
+ <para>
+ The primary clipboard can be accessed by the <guimenuitem>Cut</guimenuitem>,
+ <guimenuitem>Copy</guimenuitem> and <guimenuitem>Paste</guimenuitem> menu
+ items in the <guimenu>Edit</guimenu> menu. The keyboard shortcuts mentioned
+ above also work.
+ </para>
+
+ <sect3 id="paste-from-history">
+ <title>Paste from History</title>
+
+ <screenshot>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="images/paste-from-history.png" format="PNG"/>
+ </imageobject>
+
+ <textobject>
+ <phrase>Paste from History</phrase>
+ </textobject>
+
+ <caption>
+ <para>Screenshot of the history menu</para>
+ </caption>
+ </mediaobject>
+ </screenshot>
+
+ <para>
+ Each time you copy text in &application;, the text is pushed onto a stack.
+ When you choose <menuchoice><guimenu>Edit</guimenu><guimenuitem>Paste Special
+ </guimenuitem><guimenuitem>Paste from History</guimenuitem></menuchoice> a
+ menu will appear below the carret with the last 10 clipboard items.
+ </para>
+
+ <para>
+ You can choose an item from the list by clicking it, using the arrow keys
+ and press enter or by pressing the numeric keyboard accelerator.
+ </para>
+ </sect3>
+
+ <sect3 id="paste-as-column">
+ <title>Paste as Column</title>
+
+ <para>
+ When the content in the primary clipboard contains multiple lines, you can paste
+ the content in the same column. &application; will then try to paste each line
+ in the clipboard at the same x-coordinate. You can access this option by
+ choosing <menuchoice><guimenu>Edit</guimenu><guimenuitem>Paste Special
+ </guimenuitem><guimenuitem>Paste as Column</guimenuitem></menuchoice>.
+ </para>
+ </sect3>
+ </sect2>
+
+ <sect2 id="auto-indent">
+ <title>Auto Indent</title>
+
+ </sect2>
+
+ <sect2 id="selections">
+ <title>Selecting Text</title>
+
+ <sect3 id="multi-and-column-selections">
+ <title>Column-Selections</title>
+ </sect3>
+
+ <sect3 id="indentation">
+ <title>Indentation</title>
+ </sect3>
+
+ <sect3 id="moving-selections">
+ <title>Moving Selections</title>
+ </sect3>
+ </sect2>
+
+ <sect2 id="search-and-replace">
+ <title>Search And Replace</title>
+
+ <sect3 id="typeahead-search">
+ <title>Typeahead Search</title>
+
+ <screenshot>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="images/typeahead-search.png" format="PNG"/>
+ </imageobject>
+
+ <textobject>
+ <phrase>Typeahead search bar</phrase>
+ </textobject>
+
+ <caption>
+ <para>Screenshot of the typeahead search bar</para>
+ </caption>
+ </mediaobject>
+ </screenshot>
+ </sect3>
+
+ <sect3 id="replace-dialog">
+ <title>Search and Replace Dialog</title>
+
+ <screenshot>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="images/find-and-replace.png" format="PNG"/>
+ </imageobject>
+
+ <textobject>
+ <phrase>Find and Replace Dialog</phrase>
+ </textobject>
+
+ <caption>
+ <para>Screenshot of the find and replace dialog</para>
+ </caption>
+ </mediaobject>
+ </screenshot>
+ </sect3>
+ </sect2>
+
+ <sect2 id="tabs">
+ <title>Tabs</title>
+
+ <screenshot>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="images/tab-size-menu.png" format="PNG"/>
+ </imageobject>
+
+ <textobject>
+ <phrase>Tab Size menu</phrase>
+ </textobject>
+
+ <caption>
+ <para>Screenshot of the tabs size menu</para>
+ </caption>
+ </mediaobject>
+ </screenshot>
+
+ <screenshot>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="images/other-tab-size.png" format="PNG"/>
+ </imageobject>
+
+ <textobject>
+ <phrase>Other tab size</phrase>
+ </textobject>
+
+ <caption>
+ <para>Screenshot of the other tab size dialog</para>
+ </caption>
+ </mediaobject>
+ </screenshot>
+
+ <sect3 id="insert-spaces">
+ <title>Insert Spaces</title>
+ </sect3>
+ </sect2>
+
+ <sect2 id="transpose">
+ <title>Transpose</title>
+ </sect2>
+
+ <sect2 id="printing">
+ <title>Printing</title>
+ </sect2>
+ </sect1>
+
+ <sect1 id="faq">
+ <title>Frequently Asked Questions</title>
+
+ <para>
+ The intent of this section is to collect the quite numerous frequently asked
+ questions that relate to working with &application;. If you know of a question that
+ is missing from this page, please <ulink type="http"
+ url="http://bugzilla.xfce.org/enter_bug.cgi?product=Mousepad&format=guided">
+ file a request</ulink>.
+ </para>
+
+ <sect3 id="faq-hidden-settings">
+ <title>Does &application; has hidden settings?</title>
+
+ <para>
+ Yes, some of the settings in the mousepadrc file are not configurable from
+ the interface and can only be modified by hand. To find the mousepadrc file
+ see <xref linkend="faq-mousepadrc" />. It is not recommended to change the
+ rc file using &application;, since it might be overwritten when you
+ close the window after editing. You must also restart &application; to
+ apply the settings. The hidden settings, starting with <varname>Misc</varname>,
+ are explained in the list below:
+ </para>
+
+ <variablelist>
+ <varlistentry>
+ <term><varname>MiscAlwaysShowTabs</varname></term>
+ <listitem>
+ <para>
+ If <literal>TRUE</literal> the tab headers will always be displayed even if only a single
+ document tab is open. This option is useful if you want to drag-and-drop the last tab to
+ another window. See <xref linkend="ordering-documents" />.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>MiscCycleTabs</varname></term>
+ <listitem>
+ <para>
+ This option controls whether you can circulate through document tabs. That is, whether
+ you are able to go from the last tab to the first tab using the <menuchoice>
+ <guimenu>Navigation</guimenu><guimenuitem>Forward</guimenuitem></menuchoice> entry
+ (or the associated keyboard shortcut), and from the first tab to the last tab using the
+ <menuchoice><guimenu>Navigation</guimenu><guimenuitem>Back</guimenuitem></menuchoice> entry.
+ The option can be either <literal>TRUE</literal> or <literal>FALSE</literal> (default).
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>MiscPathInTitle</varname></term>
+ <listitem>
+ <para>
+ This option controls whether the full document path is displayed in the window title.
+ By default, <literal>FALSE</literal>, only the filename is shown. The option can be either
+ <literal>TRUE</literal> or <literal>FALSE</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>MiscRecentMenuItems</varname></term>
+ <listitem>
+ <para>
+ This option controls the maximum number of items shown in the
+ <menuchoice><guimenu>File</guimenu><guimenuitem>Open Recent</guimenuitem>
+ </menuchoice> menu. When you set the value to <literal>0</literal>, so
+ entire menu will be hidden. The integer can be in a range of <literal>0</literal>
+ ..<literal>4096</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>MiscRememberGeometry</varname></term>
+ <listitem>
+ <para>
+ Whether &application; should remember the size of windows and apply that size to
+ new windows. If <literal>TRUE</literal> (default) the width and height are saved
+ to <varname>WindowWidth</varname> and <varname>WindowHeight</varname>. If
+ <literal>FALSE</literal> the user may specify the start size in
+ <varname>WindowWidth</varname> and <varname>WindowHeight</varname>.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>MiscDefaultTabSizes</varname></term>
+ <listitem>
+ <para>
+ This is the default list of tab sizes displayed in the <menuchoice>
+ <guimenu>Document</guimenu><guimenuitem>Tab Size</guimenuitem></menuchoice>
+ menu. Each size must be in a range of <literal>1</literal>..<literal>32</literal>.
+ By defaul the value is <literal>2,3,4,8</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </sect3>
+
+ <sect3 id="faq-mousepadrc">
+ <title>Where does &application; store its preferences?</title>
+
+ <para>
+ &application; stores the user configurable preferences (and hidden settings) in
+ an <filename>.ini</filename> file, which is located at
+ <filename>$XDG_CONFIG_HOME/&application;/mousepadrc</filename>. If you make changes
+ to this file, make sure you restart &application;. It's also not recommended to
+ use &application; to edito this file, since it might overwrite it after you've
+ changed the file.
+ </para>
+ </sect3>
+
+ <sect3 id="faq-assign-keyboard-shortcuts">
+ <title>How do I assign different keyboard shortcuts?</title>
+
+ <para>
+ If you want to rebind a shortcut, &application; supports the standard GTK+ way
+ of changing shortcuts: simply hover over the menu option with the mouse
+ pointer and press the keyboard shortcut you want to rebind it to.
+ </para>
+
+ <para>
+ To delete a keyboard assignment, press the <keycap>Backspace</keycap> key
+ while you are on the menu entry.
+ </para>
+
+ <para>
+ If the shortcut doesn't change, then you need to enable the feature in
+ GTK+. This can be achieved in 3 ways:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ If you are running Xfce 4.3 or above then you can enable <guilabel>Editable
+ menu accelerators</guilabel> in the <guilabel>User Interface Preferences</guilabel>
+ dialog.
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ If you are running GNOME then you can enable <guilabel>Editable menu
+ accelerators</guilabel> in the <guilabel>Menu and Toolbars</guilabel> control
+ center dialog.
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Otherwise put the following in your <filename>~/.gtkrc-2.0</filename> file
+ (create the file if it doesn't exist):<screen>gtk-can-change-accels=1</screen>
+ </para>
+ </listitem>
+ </itemizedlist>
+ </sect3>
+
+ <sect3 id="faq-store-keyboard-shortcuts">
+ <title>Where does &application; store the keyboard shortcuts?</title>
+
+ <para>
+ The custom keyboard shortcuts are stored in the standard GTK+ accel map format in a
+ file located at <filename>$XDG_CONFIG_HOME/&application;/accels.scm</filename>. Lines starting
+ with <literal>;</literal> are comments. See the GTK+ documentation for details about the
+ file format.
+ </para>
+
+ <para>
+ If you are a packager or a system administrator and want to provide a system wide default
+ for the keyboard shortcuts, that is different from the default shortcuts in &application;, you
+ can create a file <filename>&application;/accels.scm</filename> in one of the <envar>$XDG_CONFIG_DIRS</envar>.
+ For example, if <filename role="directory">/etc/xdg</filename> is part of <envar>$XDG_CONFIG_DIRS</envar>
+ (the default for most Linux distributions), you can install system wide defaults to
+ <filename>/etc/xdg/&application;/accels.scm</filename>. &application; will then load shortcuts from
+ this file on first startup.
+ </para>
+ </sect3>
+ </sect1>
+</article>
+<!--
+ vim:set ts=2 sw=2 et ai encoding=UTF-8:
+-->
diff --git a/docs/manual/C/images/Makefile.am b/docs/manual/C/images/Makefile.am
new file mode 100644
index 0000000..c329f2b
--- /dev/null
+++ b/docs/manual/C/images/Makefile.am
@@ -0,0 +1,15 @@
+imagesdir = $(datadir)/doc/Mousepad/html/C/images
+images_DATA = \
+ find-and-replace.png \
+ go-to.png \
+ main-window.png \
+ Makefile.am \
+ other-tab-size.png \
+ paste-from-history.png \
+ tab-size-menu.png \
+ typeahead-search.png
+
+EXTRA_DIST = \
+ $(images_DATA)
+
+# vi:set ts=8 sw=8 noet ai nocindent syntax=automake:
diff --git a/docs/manual/C/images/find-and-replace.png b/docs/manual/C/images/find-and-replace.png
new file mode 100644
index 0000000..9e8648a
Binary files /dev/null and b/docs/manual/C/images/find-and-replace.png differ
diff --git a/docs/manual/C/images/go-to.png b/docs/manual/C/images/go-to.png
new file mode 100644
index 0000000..4fe1d3c
Binary files /dev/null and b/docs/manual/C/images/go-to.png differ
diff --git a/docs/manual/C/images/main-window.png b/docs/manual/C/images/main-window.png
new file mode 100644
index 0000000..2ac0a27
Binary files /dev/null and b/docs/manual/C/images/main-window.png differ
diff --git a/docs/manual/C/images/other-tab-size.png b/docs/manual/C/images/other-tab-size.png
new file mode 100644
index 0000000..651d095
Binary files /dev/null and b/docs/manual/C/images/other-tab-size.png differ
diff --git a/docs/manual/C/images/paste-from-history.png b/docs/manual/C/images/paste-from-history.png
new file mode 100644
index 0000000..dd2d757
Binary files /dev/null and b/docs/manual/C/images/paste-from-history.png differ
diff --git a/docs/manual/C/images/tab-size-menu.png b/docs/manual/C/images/tab-size-menu.png
new file mode 100644
index 0000000..942063c
Binary files /dev/null and b/docs/manual/C/images/tab-size-menu.png differ
diff --git a/docs/manual/C/images/typeahead-search.png b/docs/manual/C/images/typeahead-search.png
new file mode 100644
index 0000000..2fc6664
Binary files /dev/null and b/docs/manual/C/images/typeahead-search.png differ
diff --git a/docs/manual/Makefile.am b/docs/manual/Makefile.am
new file mode 100644
index 0000000..817eaa6
--- /dev/null
+++ b/docs/manual/Makefile.am
@@ -0,0 +1,12 @@
+SUBDIRS = \
+ C
+
+cssdir = $(datadir)/doc/Mousepad/html
+css_DATA = \
+ mousepad.css
+
+EXTRA_DIST = \
+ $(css_DATA) \
+ mousepad.xsl
+
+# vi:set ts=8 sw=8 noet ai nocindent syntax=automake:
diff --git a/docs/manual/mousepad.css b/docs/manual/mousepad.css
new file mode 100644
index 0000000..de1ed89
--- /dev/null
+++ b/docs/manual/mousepad.css
@@ -0,0 +1,99 @@
+body address
+{
+ line-height: 1.3;
+ margin: .6em 0;
+}
+
+body blockquote
+{
+ margin-top: .75em;
+ line-height: 1.5;
+ margin-bottom: .75em;
+}
+
+html body
+{
+ margin: 1em 8% 1em 10%;
+ line-height: 1.2;
+ background-color: #ffffff;
+}
+
+body pre
+{
+ margin: .75em 0;
+ line-height: 1.3;
+ color: #4f3f3f;
+ font-weight: bold;
+}
+
+body div
+{
+ margin: 0;
+}
+
+dl
+{
+ margin: .8em 0;
+ line-height: 1.2;
+}
+
+.legalnotice
+{
+ font-size: small;
+ font-variant: small-caps;
+}
+
+h1,h2,h3,h4,h5,h6,
+div.example p b,
+.question,
+div.table p b,
+div.procedure p b
+{
+ color: #990000;
+}
+
+.option
+{
+ color: #0000ca;
+ font-weight: bold;
+}
+
+.parameter
+{
+ color: #007a00;
+ font-weight: bold;
+}
+
+a
+{
+ color: #000000;
+}
+
+a:hover
+{
+ color: #3c3c3c;
+ border-bottom: 1px dotted #dc0000;
+}
+
+hr
+{
+ background-color: #9c9c9c;
+ border-style: none;
+ height: 1px;
+}
+
+ul li
+{
+ list-style-type: square;
+}
+
+.programlisting, .screen
+{
+ background-color: #F8F9FD;
+ border-color: #907777;
+ border-width: 1px;
+ border-style: solid;
+ padding: 0.5em;
+}
+
+/* vim:set ts=2 sw=2 et ai: */
diff --git a/docs/manual/mousepad.xsl b/docs/manual/mousepad.xsl
new file mode 100644
index 0000000..229d192
--- /dev/null
+++ b/docs/manual/mousepad.xsl
@@ -0,0 +1,254 @@
+<?xml version='1.0'?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ version='1.0'
+ xmlns="http://www.w3.org/TR/xhtml1/transitional"
+ exclude-result-prefixes="#default">
+
+<xsl:import href="http://docbook.sourceforge.net/release/xsl/current/html/chunk.xsl"/>
+
+<!-- Use stylesheet -->
+<xsl:param name="html.stylesheet" select="'../mousepad.css'"/>
+
+<!-- labels and numbering -->
+<xsl:param name="autotoc.label.separator" select="'. '"/>
+<xsl:param name="chapter.autolabel" select="1"/>
+
+<!-- Don't force the use of index.html as root filename -->
+<xsl:param name="root.filename" select="''"/>
+
+<!-- Use element id (if present) as file name -->
+<xsl:variable name="use.id.as.filename">1</xsl:variable>
+
+<xsl:template match="releaseinfo" mode="titlepage.mode">
+ <span class="{name(.)}">
+ <br/>
+ <xsl:apply-templates mode="titlepage.mode"/>
+ <br/>
+ </span>
+</xsl:template>
+
+<!-- Use graphics in admonitions (note, warning, etc) -->
+<xsl:variable name="admon.graphics">0</xsl:variable>
+
+<xsl:param name="admon.style">
+ <xsl:text>text-align: left;</xsl:text></xsl:param>
+
+<xsl:variable name="admon.graphics.path">stylesheet-images/</xsl:variable>
+
+<xsl:variable name="admon.graphics.extension">.gif</xsl:variable>
+
+<xsl:param name="table.border.thickness" select="'0.2pt'"/>
+
+<xsl:param name="graphic.default.extension" select="png"/>
+
+<!-- This requires an adapted template for tgroup (see end of stylesheet) -->
+<xsl:attribute-set name="table.style">
+ <xsl:attribute name="bgcolor">#fdf9f8</xsl:attribute>
+ <xsl:attribute name="cellspacing">0</xsl:attribute>
+ <xsl:attribute name="cellpadding">4</xsl:attribute>
+</xsl:attribute-set>
+
+
+<xsl:param name="generate.legalnotice.link" select="0"/>
+
+<!-- set font styles for various tags -->
+<xsl:template match="guibutton">
+<xsl:call-template name="inline.boldseq"/>
+</xsl:template>
+
+<xsl:template match="guiicon">
+<xsl:call-template name="inline.boldseq"/>
+</xsl:template>
+
+<xsl:template match="guilabel">
+<xsl:call-template name="inline.boldseq"/>
+</xsl:template>
+
+<xsl:template match="guimenu">
+<xsl:call-template name="inline.boldseq"/>
+</xsl:template>
+
+<xsl:template match="guimenuitem">
+<xsl:call-template name="inline.boldseq"/>
+</xsl:template>
+
+<xsl:template match="guisubmenu">
+<xsl:call-template name="inline.boldseq"/>
+</xsl:template>
+
+<xsl:template match="application">
+<xsl:call-template name="inline.boldmonoseq"/>
+</xsl:template>
+
+<xsl:template match="caption">
+<xsl:call-template name="inline.boldseq"/>
+</xsl:template>
+
+<!-- Adapted template for tgroup. The only change is the addition of -->
+<!-- table.style attributes -->
+<xsl:template match="tgroup">
+ <table xsl:use-attribute-sets="table.style">
+ <xsl:choose>
+ <!-- If there's a <?dbhtml table-summary="foo"?> PI, use it for
+ the HTML table summary attribute -->
+ <xsl:when test="processing-instruction('dbhtml')">
+ <xsl:variable name="summary">
+ <xsl:call-template name="dbhtml-attribute">
+ <xsl:with-param name="pis"
+ select="processing-instruction('dbhtml')[1]"/>
+ <xsl:with-param name="attribute" select="'table-summary'"/>
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:if test="$summary != ''">
+ <xsl:attribute name="summary">
+ <xsl:value-of select="$summary"/>
+ </xsl:attribute>
+ </xsl:if>
+ </xsl:when>
+ <!-- Otherwise, if there's a title, use that -->
+ <xsl:when test="../title">
+ <xsl:attribute name="summary">
+ <xsl:value-of select="string(../title)"/>
+ </xsl:attribute>
+ </xsl:when>
+ <!-- Otherwise, forget the whole idea -->
+ <xsl:otherwise><!-- nevermind --></xsl:otherwise>
+ </xsl:choose>
+
+ <xsl:if test="../@pgwide=1">
+ <xsl:attribute name="width">100%</xsl:attribute>
+ </xsl:if>
+
+ <xsl:choose>
+ <xsl:when test="../@frame='none'">
+ <xsl:attribute name="border">0</xsl:attribute>
+ </xsl:when>
+ <xsl:when test="$table.borders.with.css != 0">
+ <xsl:attribute name="border">0</xsl:attribute>
+ <xsl:choose>
+ <xsl:when test="../@frame='topbot' or ../@frame='top'">
+ <xsl:attribute name="style">
+ <xsl:call-template name="border">
+ <xsl:with-param name="side" select="'top'"/>
+ </xsl:call-template>
+ </xsl:attribute>
+ </xsl:when>
+ <xsl:when test="../@frame='sides'">
+ <xsl:attribute name="style">
+ <xsl:call-template name="border">
+ <xsl:with-param name="side" select="'left'"/>
+ </xsl:call-template>
+ <xsl:call-template name="border">
+ <xsl:with-param name="side" select="'right'"/>
+ </xsl:call-template>
+ </xsl:attribute>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:attribute name="border">1</xsl:attribute>
+ </xsl:otherwise>
+ </xsl:choose>
+
+ <xsl:variable name="colgroup">
+ <colgroup>
+ <xsl:call-template name="generate.colgroup">
+ <xsl:with-param name="cols" select="@cols"/>
+ </xsl:call-template>
+ </colgroup>
+ </xsl:variable>
+
+ <xsl:variable name="explicit.table.width">
+ <xsl:call-template name="dbhtml-attribute">
+ <xsl:with-param name="pis"
+ select="../processing-instruction('dbhtml')[1]"/>
+ <xsl:with-param name="attribute" select="'table-width'"/>
+ </xsl:call-template>
+ </xsl:variable>
+
+ <xsl:variable name="table.width">
+ <xsl:choose>
+ <xsl:when test="$explicit.table.width != ''">
+ <xsl:value-of select="$explicit.table.width"/>
+ </xsl:when>
+ <xsl:when test="$default.table.width = ''">
+ <xsl:text>100%</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$default.table.width"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <xsl:if test="$default.table.width != ''
+ or $explicit.table.width != ''">
+ <xsl:attribute name="width">
+ <xsl:choose>
+ <xsl:when test="contains($table.width, '%')">
+ <xsl:value-of select="$table.width"/>
+ </xsl:when>
+ <xsl:when test="$use.extensions != 0
+ and $tablecolumns.extension != 0">
+ <xsl:choose>
+ <xsl:when test="function-available('stbl:convertLength')">
+ <xsl:value-of select="stbl:convertLength($table.width)"/>
+ </xsl:when>
+ <xsl:when test="function-available('xtbl:convertLength')">
+ <xsl:value-of select="xtbl:convertLength($table.width)"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:message terminate="yes">
+ <xsl:text>No convertLength function available.</xsl:text>
+ </xsl:message>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$table.width"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:attribute>
+ </xsl:if>
+
+ <xsl:choose>
+ <xsl:when test="$use.extensions != 0
+ and $tablecolumns.extension != 0">
+ <xsl:choose>
+ <xsl:when test="function-available('stbl:adjustColumnWidths')">
+ <xsl:copy-of select="stbl:adjustColumnWidths($colgroup)"/>
+ </xsl:when>
+ <xsl:when test="function-available('xtbl:adjustColumnWidths')">
+ <xsl:copy-of select="xtbl:adjustColumnWidths($colgroup)"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:message terminate="yes">
+ <xsl:text>No adjustColumnWidths function available.</xsl:text>
+ </xsl:message>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:copy-of select="$colgroup"/>
+ </xsl:otherwise>
+ </xsl:choose>
+
+ <xsl:apply-templates select="thead"/>
+ <xsl:apply-templates select="tbody"/>
+ <xsl:apply-templates select="tfoot"/>
+
+ <xsl:if test=".//footnote">
+ <tbody class="footnotes">
+ <tr>
+ <td colspan="{@cols}">
+ <xsl:apply-templates select=".//footnote"
+ mode="table.footnote.mode"/>
+ </td>
+ </tr>
+ </tbody>
+ </xsl:if>
+ </table>
+</xsl:template>
+
+
+</xsl:stylesheet>
+
diff --git a/icons/16x16/Makefile.am b/icons/16x16/Makefile.am
new file mode 100644
index 0000000..a8e5cb7
--- /dev/null
+++ b/icons/16x16/Makefile.am
@@ -0,0 +1,13 @@
+iconsdir = $(datadir)/icons/hicolor/16x16/apps
+icons_DATA = \
+ Mousepad.png
+
+# install symlink
+install-data-local:
+ $(mkinstalldirs) $(DESTDIR)$(iconsdir)
+ -( cd $(DESTDIR)$(iconsdir) ; ln -sf Mousepad.png accessories-text-editor.png )
+
+EXTRA_DIST = \
+ $(icons_DATA)
+
+# vi:set ts=8 sw=8 noet ai nocindent syntax=automake:
diff --git a/icons/16x16/Mousepad.png b/icons/16x16/Mousepad.png
new file mode 100644
index 0000000..188e1c1
Binary files /dev/null and b/icons/16x16/Mousepad.png differ
diff --git a/icons/24x24/Makefile.am b/icons/24x24/Makefile.am
new file mode 100644
index 0000000..c22ae68
--- /dev/null
+++ b/icons/24x24/Makefile.am
@@ -0,0 +1,13 @@
+iconsdir = $(datadir)/icons/hicolor/24x24/apps
+icons_DATA = \
+ Mousepad.png
+
+# install symlink
+install-data-local:
+ $(mkinstalldirs) $(DESTDIR)$(iconsdir)
+ -( cd $(DESTDIR)$(iconsdir) ; ln -sf Mousepad.png accessories-text-editor.png )
+
+EXTRA_DIST = \
+ $(icons_DATA)
+
+# vi:set ts=8 sw=8 noet ai nocindent syntax=automake:
diff --git a/icons/24x24/Mousepad.png b/icons/24x24/Mousepad.png
new file mode 100644
index 0000000..bdbb88e
Binary files /dev/null and b/icons/24x24/Mousepad.png differ
diff --git a/icons/Makefile.am b/icons/Makefile.am
new file mode 100644
index 0000000..21932ab
--- /dev/null
+++ b/icons/Makefile.am
@@ -0,0 +1,19 @@
+SUBDIRS = \
+ 16x16 \
+ 24x24 \
+ scalable
+
+gtk_update_icon_cache = gtk-update-icon-cache -f -t $(datadir)/icons/hicolor
+
+install-data-hook:
+ @-if test -z "$(DESTDIR)"; then \
+ echo "Updating Gtk icon cache."; \
+ $(gtk_update_icon_cache); \
+ else \
+ echo "*** Icon cache not updated. Remember to run:"; \
+ echo "***"; \
+ echo "*** $(gtk_update_icon_cache)"; \
+ echo "***"; \
+ fi
+
+# vi:set ts=8 sw=8 noet ai nocindent syntax=automake:
diff --git a/icons/scalable/Makefile.am b/icons/scalable/Makefile.am
new file mode 100644
index 0000000..2beabc7
--- /dev/null
+++ b/icons/scalable/Makefile.am
@@ -0,0 +1,13 @@
+iconsdir = $(datadir)/icons/hicolor/scalable/apps
+icons_DATA = \
+ Mousepad.svg
+
+# install symlink
+install-data-local:
+ $(mkinstalldirs) $(DESTDIR)$(iconsdir)
+ -( cd $(DESTDIR)$(iconsdir) ; ln -sf Mousepad.svg accessories-text-editor.svg )
+
+EXTRA_DIST = \
+ $(icons_DATA)
+
+# vi:set ts=8 sw=8 noet ai nocindent syntax=automake:
diff --git a/icons/scalable/Mousepad.svg b/icons/scalable/Mousepad.svg
new file mode 100644
index 0000000..aa7188e
--- /dev/null
+++ b/icons/scalable/Mousepad.svg
@@ -0,0 +1,552 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ inkscape:export-ydpi="90.000000"
+ inkscape:export-xdpi="90.000000"
+ inkscape:export-filename="/home/jimmac/Desktop/wi-fi.png"
+ width="48px"
+ height="48px"
+ id="svg11300"
+ sodipodi:version="0.32"
+ inkscape:version="0.43+devel"
+ sodipodi:docbase="/home/jimmac/src/cvs/tango-icon-theme/scalable/apps"
+ sodipodi:docname="accessories-text-editor.svg">
+ <defs
+ id="defs3">
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5060"
+ id="radialGradient6719"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(-2.774389,0,0,1.969706,112.7623,-872.8854)"
+ cx="605.71429"
+ cy="486.64789"
+ fx="605.71429"
+ fy="486.64789"
+ r="117.14286" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient5060">
+ <stop
+ style="stop-color:black;stop-opacity:1;"
+ offset="0"
+ id="stop5062" />
+ <stop
+ style="stop-color:black;stop-opacity:0;"
+ offset="1"
+ id="stop5064" />
+ </linearGradient>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5060"
+ id="radialGradient6717"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.774389,0,0,1.969706,-1891.633,-872.8854)"
+ cx="605.71429"
+ cy="486.64789"
+ fx="605.71429"
+ fy="486.64789"
+ r="117.14286" />
+ <linearGradient
+ id="linearGradient5048">
+ <stop
+ style="stop-color:black;stop-opacity:0;"
+ offset="0"
+ id="stop5050" />
+ <stop
+ id="stop5056"
+ offset="0.5"
+ style="stop-color:black;stop-opacity:1;" />
+ <stop
+ style="stop-color:black;stop-opacity:0;"
+ offset="1"
+ id="stop5052" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5048"
+ id="linearGradient6715"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)"
+ x1="302.85715"
+ y1="366.64789"
+ x2="302.85715"
+ y2="609.50507" />
+ <linearGradient
+ id="linearGradient2994">
+ <stop
+ style="stop-color:#000000;stop-opacity:1;"
+ offset="0"
+ id="stop2996" />
+ <stop
+ style="stop-color:#c9c9c9;stop-opacity:1;"
+ offset="1"
+ id="stop2998" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient2984">
+ <stop
+ style="stop-color:#e7e2b8;stop-opacity:1;"
+ offset="0"
+ id="stop2986" />
+ <stop
+ style="stop-color:#e7e2b8;stop-opacity:0;"
+ offset="1"
+ id="stop2988" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2974">
+ <stop
+ style="stop-color:#c1c1c1;stop-opacity:1;"
+ offset="0"
+ id="stop2976" />
+ <stop
+ style="stop-color:#acacac;stop-opacity:1;"
+ offset="1"
+ id="stop2978" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2966">
+ <stop
+ style="stop-color:#ffd1d1;stop-opacity:1;"
+ offset="0"
+ id="stop2968" />
+ <stop
+ id="stop3006"
+ offset="0.5"
+ style="stop-color:#ff1d1d;stop-opacity:1;" />
+ <stop
+ style="stop-color:#6f0000;stop-opacity:1;"
+ offset="1"
+ id="stop2970" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2919">
+ <stop
+ style="stop-color:#a3a4a0;stop-opacity:1;"
+ offset="0"
+ id="stop2921" />
+ <stop
+ style="stop-color:#888a85;stop-opacity:1;"
+ offset="1"
+ id="stop2923" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2873">
+ <stop
+ style="stop-color:#939393;stop-opacity:1;"
+ offset="0"
+ id="stop2875" />
+ <stop
+ style="stop-color:#424242;stop-opacity:1;"
+ offset="1"
+ id="stop2877" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient2865">
+ <stop
+ style="stop-color:#000000;stop-opacity:1;"
+ offset="0"
+ id="stop2867" />
+ <stop
+ style="stop-color:#000000;stop-opacity:0;"
+ offset="1"
+ id="stop2869" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2855">
+ <stop
+ style="stop-color:#dfdfdf;stop-opacity:1;"
+ offset="0"
+ id="stop2857" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="1"
+ id="stop2859" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2855"
+ id="linearGradient2861"
+ x1="21.043484"
+ y1="42.83337"
+ x2="14.283642"
+ y2="6.8333683"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.137871,0.000000,0.000000,1.000000,-2.660884,0.000000)" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2865"
+ id="radialGradient2871"
+ cx="23.5625"
+ cy="40.4375"
+ fx="23.5625"
+ fy="40.4375"
+ r="19.5625"
+ gradientTransform="matrix(1.000000,0.000000,0.000000,0.348243,0.000000,26.35543)"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2873"
+ id="linearGradient2879"
+ x1="26.612417"
+ y1="28.083368"
+ x2="26.228401"
+ y2="42.83337"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2919"
+ id="linearGradient2925"
+ x1="6"
+ y1="7.5624999"
+ x2="40.984375"
+ y2="7.5624999"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2966"
+ id="linearGradient2972"
+ x1="48.90625"
+ y1="17.376184"
+ x2="50.988335"
+ y2="22.250591"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-5.669292,0.000000)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2974"
+ id="linearGradient2980"
+ x1="46"
+ y1="19.8125"
+ x2="47.6875"
+ y2="22.625"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-5.669292,0.000000)" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2984"
+ id="radialGradient2990"
+ cx="29.053354"
+ cy="27.640751"
+ fx="29.053354"
+ fy="27.640751"
+ r="3.2408544"
+ gradientTransform="matrix(2.923565,-3.911409e-24,2.471769e-23,2.029717,-61.55532,-27.88417)"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2994"
+ id="linearGradient3000"
+ x1="25.71875"
+ y1="31.046875"
+ x2="25.514589"
+ y2="30.703125"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-5.825542,0.125000)" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2865"
+ id="radialGradient3010"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.000000,0.000000,0.000000,0.348243,1.439818e-16,26.35543)"
+ cx="23.5625"
+ cy="40.4375"
+ fx="23.5625"
+ fy="40.4375"
+ r="19.5625" />
+ </defs>
+ <sodipodi:namedview
+ stroke="#c4a000"
+ fill="#edd400"
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="0.25490196"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1"
+ inkscape:cx="14.928934"
+ inkscape:cy="7.6822472"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:grid-bbox="true"
+ inkscape:document-units="px"
+ inkscape:showpageshadow="false"
+ inkscape:window-width="872"
+ inkscape:window-height="659"
+ inkscape:window-x="195"
+ inkscape:window-y="221" />
+ <metadata
+ id="metadata4">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:creator>
+ <cc:Agent>
+ <dc:title>Jakub Steiner</dc:title>
+ </cc:Agent>
+ </dc:creator>
+ <dc:source>http://jimmac.musichall.cz</dc:source>
+ <cc:license
+ rdf:resource="http://creativecommons.org/licenses/by-sa/2.0/" />
+ <dc:title>Text Editor</dc:title>
+ </cc:Work>
+ <cc:License
+ rdf:about="http://creativecommons.org/licenses/by-sa/2.0/">
+ <cc:permits
+ rdf:resource="http://web.resource.org/cc/Reproduction" />
+ <cc:permits
+ rdf:resource="http://web.resource.org/cc/Distribution" />
+ <cc:requires
+ rdf:resource="http://web.resource.org/cc/Notice" />
+ <cc:requires
+ rdf:resource="http://web.resource.org/cc/Attribution" />
+ <cc:permits
+ rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
+ <cc:requires
+ rdf:resource="http://web.resource.org/cc/ShareAlike" />
+ </cc:License>
+ </rdf:RDF>
+ </metadata>
+ <g
+ id="layer1"
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer">
+ <g
+ transform="matrix(2.417561e-2,0,0,2.086758e-2,45.12765,40.1536)"
+ id="g6707">
+ <rect
+ style="opacity:0.40206185;color:black;fill:url(#linearGradient6715);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ id="rect6709"
+ width="1339.6335"
+ height="478.35718"
+ x="-1559.2523"
+ y="-150.69685" />
+ <path
+ style="opacity:0.40206185;color:black;fill:url(#radialGradient6717);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ d="M -219.61876,-150.68038 C -219.61876,-150.68038 -219.61876,327.65041 -219.61876,327.65041 C -76.744594,328.55086 125.78146,220.48075 125.78138,88.454235 C 125.78138,-43.572302 -33.655436,-150.68036 -219.61876,-150.68038 z "
+ id="path6711"
+ sodipodi:nodetypes="cccc" />
+ <path
+ sodipodi:nodetypes="cccc"
+ id="path6713"
+ d="M -1559.2523,-150.68038 C -1559.2523,-150.68038 -1559.2523,327.65041 -1559.2523,327.65041 C -1702.1265,328.55086 -1904.6525,220.48075 -1904.6525,88.454235 C -1904.6525,-43.572302 -1745.2157,-150.68036 -1559.2523,-150.68038 z "
+ style="opacity:0.40206185;color:black;fill:url(#radialGradient6719);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+ </g>
+ <path
+ style="color:#000000;fill:url(#linearGradient2861);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient2879);stroke-width:0.99999982;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ d="M 7.1638699,4.5063726 L 39.813122,4.5063726 C 40.575699,4.5063726 41.189615,5.0388241 41.189615,5.7002099 C 41.189615,5.7002099 43.590945,39.868907 43.590945,39.868907 C 43.590945,39.868907 43.603403,42.216529 43.603403,42.216529 C 43.603403,42.877915 42.989488,43.410366 42.226911,43.410366 L 4.750081,43.410366 C 3.9875042,43.410366 3.3735887,42.877915 3.3735887,42.216529 L 3.3624173,40.049613 L 5.7873775,5.7002099 C 5.7873775,5.0388241 6.4012931,4.5063726 7.1638699,4.5063726 z "
+ id="rect1975"
+ sodipodi:nodetypes="ccccccccccc" />
+ <path
+ transform="matrix(0.616613,0.000000,0.000000,0.440367,10.61425,13.94266)"
+ d="M 43.125 40.4375 A 19.5625 6.8125 0 1 1 4,40.4375 A 19.5625 6.8125 0 1 1 43.125 40.4375 z"
+ sodipodi:ry="6.8125"
+ sodipodi:rx="19.5625"
+ sodipodi:cy="40.4375"
+ sodipodi:cx="23.5625"
+ id="path3008"
+ style="opacity:0.31578944;color:#000000;fill:url(#radialGradient3010);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ sodipodi:type="arc" />
+ <rect
+ style="opacity:1;color:#000000;fill:#a4a4a4;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ id="rect2851"
+ width="39.048077"
+ height="3.0714951"
+ x="3.9770372"
+ y="39.868271"
+ rx="0.67937863"
+ ry="0.67937863" />
+ <path
+ style="opacity:1;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ d="M 3.9267507,40.442796 C 3.9267507,40.442796 4.0776125,39.912466 4.6307727,39.868272 L 42.195375,39.868272 C 42.949684,39.868272 42.999971,40.619573 42.999971,40.619573 C 42.999971,40.619573 43.02357,39 41.7161,39 L 5.3042159,39 C 4.2984702,39.088388 3.9267507,39.779883 3.9267507,40.442796 z "
+ id="path2853"
+ sodipodi:nodetypes="ccccccc" />
+ <path
+ style="opacity:1;color:#000000;fill:url(#linearGradient2925);fill-opacity:1.0;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ d="M 6.25,5.7343749 L 6,10.125 C 6,10.125 6.3125,8.9999999 7,8.9999999 L 40.125,8.9999999 C 40.828125,8.9843749 40.859375,9.3124999 40.984375,9.8281249 C 40.984375,9.8281249 40.734375,5.9531249 40.734375,5.9531249 C 40.703125,5.4062499 40.515625,4.9999999 39.953125,4.9999999 L 7.0625,4.9999999 C 6.609375,4.9999999 6.296875,5.3437499 6.25,5.7343749 z "
+ id="path2915"
+ sodipodi:nodetypes="ccccccccc" />
+ <path
+ sodipodi:nodetypes="ccccccccccc"
+ id="path2917"
+ d="M 7.8126474,5.5404503 L 38.944983,5.5404503 C 39.66702,5.5404503 40.2483,5.3883462 40.2483,6.014572 C 40.2483,6.014572 42.521973,39.023077 42.521973,39.023077 C 42.521973,39.023077 42.622156,41.732033 42.622156,41.732033 C 42.622156,42.358259 42.48282,42.376269 41.760782,42.376269 L 4.8620444,42.376269 C 4.4493662,42.376269 4.4426114,42.269871 4.4426114,41.864615 L 4.4320338,39.194177 L 6.7280807,6.045822 C 6.7280807,5.4195962 7.09061,5.5404503 7.8126474,5.5404503 z "
+ style="color:#000000;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.99999946;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;opacity:0.43859649" />
+ <g
+ id="g2950">
+ <rect
+ ry="1"
+ rx="1"
+ y="2.5"
+ x="8.5"
+ height="5"
+ width="2"
+ id="rect2899"
+ style="opacity:1;color:#000000;fill:#fce94f;fill-opacity:1;fill-rule:evenodd;stroke:#886f00;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+ <rect
+ style="opacity:1;color:#000000;fill:#fce94f;fill-opacity:1;fill-rule:evenodd;stroke:#886f00;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ id="rect2901"
+ width="2"
+ height="5"
+ x="12.5"
+ y="2.5"
+ rx="1"
+ ry="1" />
+ <rect
+ ry="1"
+ rx="1"
+ y="2.5"
+ x="16.5"
+ height="5"
+ width="2"
+ id="rect2903"
+ style="opacity:1;color:#000000;fill:#fce94f;fill-opacity:1;fill-rule:evenodd;stroke:#886f00;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+ <rect
+ style="opacity:1;color:#000000;fill:#fce94f;fill-opacity:1;fill-rule:evenodd;stroke:#886f00;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ id="rect2905"
+ width="2"
+ height="5"
+ x="20.5"
+ y="2.5"
+ rx="1"
+ ry="1" />
+ <rect
+ ry="1"
+ rx="1"
+ y="2.5"
+ x="24.5"
+ height="5"
+ width="2"
+ id="rect2907"
+ style="opacity:1;color:#000000;fill:#fce94f;fill-opacity:1;fill-rule:evenodd;stroke:#886f00;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+ <rect
+ style="opacity:1;color:#000000;fill:#fce94f;fill-opacity:1;fill-rule:evenodd;stroke:#886f00;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ id="rect2909"
+ width="2"
+ height="5"
+ x="28.5"
+ y="2.5"
+ rx="1"
+ ry="1" />
+ <rect
+ ry="1"
+ rx="1"
+ y="2.5"
+ x="32.5"
+ height="5"
+ width="2"
+ id="rect2911"
+ style="opacity:1;color:#000000;fill:#fce94f;fill-opacity:1;fill-rule:evenodd;stroke:#886f00;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+ <rect
+ style="opacity:1;color:#000000;fill:#fce94f;fill-opacity:1;fill-rule:evenodd;stroke:#886f00;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ id="rect2913"
+ width="2"
+ height="5"
+ x="36.5"
+ y="2.5"
+ rx="1"
+ ry="1" />
+ </g>
+ <g
+ id="g2941">
+ <rect
+ y="12"
+ x="9"
+ height="1"
+ width="29"
+ id="rect2927"
+ style="opacity:0.28070175;color:#000000;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+ <rect
+ style="opacity:0.28070176;color:#000000;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ id="rect2929"
+ width="29"
+ height="1"
+ x="9"
+ y="14.981792" />
+ <rect
+ y="18.003939"
+ x="9"
+ height="1"
+ width="13"
+ id="rect2931"
+ style="opacity:0.28070176;color:#000000;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+ <rect
+ style="opacity:0.28070176;color:#000000;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ id="rect2933"
+ width="29"
+ height="1"
+ x="9"
+ y="22.985731" />
+ <rect
+ y="26.007877"
+ x="9"
+ height="1"
+ width="29"
+ id="rect2935"
+ style="opacity:0.28070176;color:#000000;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+ <rect
+ style="opacity:0.28070176;color:#000000;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ id="rect2937"
+ width="29"
+ height="1"
+ x="9"
+ y="29.030024" />
+ <rect
+ y="32.05217"
+ x="9"
+ height="1"
+ width="8"
+ id="rect2939"
+ style="opacity:0.28070176;color:#000000;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+ </g>
+ <path
+ style="opacity:1;color:#000000;fill:#cb9022;fill-opacity:1;fill-rule:evenodd;stroke:#5c410c;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ d="M 17.34116,32.5 L 22.96616,26.875 L 43.059909,17.125 C 46.309909,15.875 48.247409,20.5 45.372409,22.125 L 25.34116,31.5 L 17.34116,32.5 z "
+ id="path2960"
+ sodipodi:nodetypes="cccccc" />
+ <path
+ sodipodi:nodetypes="czcczcc"
+ id="path2964"
+ d="M 38.330708,20 C 38.330708,20 39.768208,20.09375 40.330708,21.34375 C 40.910201,22.631511 40.330708,24 40.330708,24 L 45.361958,21.53125 C 45.361958,21.53125 46.81399,20.649883 46.018208,18.6875 C 45.233296,16.751923 43.330708,17.53125 43.330708,17.53125 L 38.330708,20 z "
+ style="opacity:1;color:#000000;fill:url(#linearGradient2972);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
+ <path
+ style="opacity:1;color:#000000;fill:url(#linearGradient2980);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ d="M 38.330708,20 C 38.330708,20 39.768208,20.09375 40.330708,21.34375 C 40.910201,22.631511 40.330708,24 40.330708,24 L 42.330708,23 C 42.330708,23 43.15774,21.681133 42.549458,20.3125 C 41.924458,18.90625 40.330708,19 40.330708,19 L 38.330708,20 z "
+ id="path2962"
+ sodipodi:nodetypes="czcczcc" />
+ <path
+ style="opacity:1;color:#000000;fill:url(#radialGradient2990);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ d="M 18.768208,31.78125 L 23.268208,27.28125 C 24.768208,28.09375 25.549458,29.4375 25.143208,31 L 18.768208,31.78125 z "
+ id="path2982"
+ sodipodi:nodetypes="cccc" />
+ <path
+ style="opacity:1;color:#000000;fill:url(#linearGradient3000);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ d="M 20.111958,30.375 L 18.486958,31.96875 L 20.830708,31.65625 C 21.049458,30.9375 20.643208,30.59375 20.111958,30.375 z "
+ id="path2992"
+ sodipodi:nodetypes="cccc" />
+ <path
+ style="opacity:1;color:#000000;fill:#ffffff;fill-opacity:0.36363639;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ d="M 23.268208,27.25 L 24.830708,28.5 L 40.218048,21.18133 C 39.773616,20.325286 38.976281,20.096733 38.314669,20.019068 L 23.268208,27.25 z "
+ id="path3002"
+ sodipodi:nodetypes="ccccc" />
+ <path
+ style="opacity:1;color:#000000;fill:#000000;fill-opacity:0.36363639;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ d="M 25.143208,31.0625 L 25.330708,30.3125 L 40.561798,23.1829 C 40.561798,23.1829 40.451638,23.796527 40.345919,23.93225 L 25.143208,31.0625 z "
+ id="path3004"
+ sodipodi:nodetypes="ccccc" />
+ </g>
+</svg>
diff --git a/mousepad/Makefile.am b/mousepad/Makefile.am
new file mode 100644
index 0000000..3dfb8ac
--- /dev/null
+++ b/mousepad/Makefile.am
@@ -0,0 +1,118 @@
+INCLUDES = \
+ -I$(top_builddir) \
+ -I$(top_srcdir) \
+ -DBINDIR=\"$(bindir)\" \
+ -DDATADIR=\"$(datadir)\" \
+ -DLIBDIR=\"$(libdir)\" \
+ -DG_LOG_DOMAIN=\"Mousepad\" \
+ -DPACKAGE_LOCALE_DIR=\"$(localedir)\" \
+ -DGDK_PIXBUF_DISABLE_SINGLE_INCLUDES \
+ -DGTK_DISABLE_SINGLE_INCLUDES \
+ $(PLATFORM_CPPFLAGS)
+
+bin_PROGRAMS = \
+ mousepad
+
+mousepad_built_sources = \
+ mousepad-marshal.c \
+ mousepad-marshal.h
+
+mousepad_SOURCES = \
+ $(mousepad_built_sources) \
+ $(mousepad_dbus_sources) \
+ main.c \
+ mousepad-application.c \
+ mousepad-application.h \
+ mousepad-dialogs.c \
+ mousepad-dialogs.h \
+ mousepad-document.c \
+ mousepad-document.h \
+ mousepad-encoding.c \
+ mousepad-encoding.h \
+ mousepad-encoding-dialog.c \
+ mousepad-encoding-dialog.h \
+ mousepad-file.c \
+ mousepad-file.h \
+ mousepad-preferences.c \
+ mousepad-preferences.h \
+ mousepad-print.c \
+ mousepad-print.h \
+ mousepad-private.h \
+ mousepad-replace-dialog.c \
+ mousepad-replace-dialog.h \
+ mousepad-search-bar.c \
+ mousepad-search-bar.h \
+ mousepad-statusbar.c \
+ mousepad-statusbar.h \
+ mousepad-view.c \
+ mousepad-view.h \
+ mousepad-util.c \
+ mousepad-util.h \
+ mousepad-window.c \
+ mousepad-window.h \
+ mousepad-window-ui.h
+
+mousepad_CFLAGS = \
+ $(GLIB_CFLAGS) \
+ $(GTK_CFLAGS) \
+ $(GTHREAD_CFLAGS) \
+ $(GTKSOURCEVIEW_CFLAGS) \
+ $(PLATFORM_CFLAGS)
+
+mousepad_LDFLAGS = \
+ -no-undefined \
+ $(PLATFORM_LDFLAGS)
+
+mousepad_LDADD = \
+ $(GLIB_LIBS) \
+ $(GTK_LIBS) \
+ $(GTHREAD_LIBS) \
+ $(GTKSOURCEVIEW_LIBS)
+
+if HAVE_DBUS
+mousepad_built_sources += \
+ mousepad-dbus-infos.h
+
+mousepad_dbus_sources = \
+ mousepad-dbus.c \
+ mousepad-dbus.h
+
+mousepad_CFLAGS += \
+ -DDBUS_API_SUBJECT_TO_CHANGE \
+ $(DBUS_CFLAGS)
+
+mousepad_LDADD += \
+ $(DBUS_LIBS)
+endif
+
+if MAINTAINER_MODE
+DISTCLEANFILES = \
+ $(mousepad_built_sources) \
+ mousepad-window-ui.h
+
+BUILT_SOURCES = \
+ $(mousepad_built_sources) \
+ mousepad-window-ui.h
+
+if HAVE_DBUS
+mousepad-dbus-infos.h: mousepad-dbus-infos.xml Makefile
+ $(AM_V_GEN) dbus-binding-tool --prefix=mousepad_dbus_service --mode=glib-server $< > $@
+endif
+
+mousepad-window-ui.h: mousepad-window-ui.xml Makefile
+ $(AM_V_GEN) exo-csource --strip-comments --strip-content --static --name=mousepad_window_ui $< > $@
+
+mousepad-marshal.h: mousepad-marshal.list Makefile
+ $(AM_V_GEN) glib-genmarshal --header --prefix=_mousepad_marshal $< > $@
+
+mousepad-marshal.c: mousepad-marshal.list Makefile
+ $(AM_V_GEN) echo "#include <mousepad/mousepad-marshal.h>" > $@ \
+ && glib-genmarshal --body --prefix=_mousepad_marshal $< >> $@
+endif
+
+EXTRA_DIST = \
+ mousepad-dbus-infos.xml \
+ mousepad-marshal.list \
+ mousepad-window-ui.xml
+
+# vi:set ts=8 sw=8 noet ai nocindent syntax=automake:
diff --git a/mousepad/main.c b/mousepad/main.c
new file mode 100644
index 0000000..22e69eb
--- /dev/null
+++ b/mousepad/main.c
@@ -0,0 +1,202 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_STDIO_H
+#include <stdio.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_LIBINTL_H
+#include <libintl.h>
+#endif
+
+#include <mousepad/mousepad-private.h>
+#include <mousepad/mousepad-application.h>
+
+#ifdef HAVE_DBUS
+#include <mousepad/mousepad-dbus.h>
+#endif
+
+
+/* globals */
+static gchar **filenames = NULL;
+static gboolean opt_version = FALSE;
+#ifdef HAVE_DBUS
+static gboolean opt_disable_server = FALSE;
+static gboolean opt_quit = FALSE;
+#endif
+
+
+
+/* command line options */
+static const GOptionEntry option_entries[] =
+{
+#ifdef HAVE_DBUS
+ { "disable-server", '\0', 0, G_OPTION_ARG_NONE, &opt_disable_server, N_("Do not register with the D-BUS session message bus"), NULL },
+ { "quit", 'q', 0, G_OPTION_ARG_NONE, &opt_quit, N_("Quit a running Mousepad instance"), NULL },
+#endif
+ { "version", 'v', 0, G_OPTION_ARG_NONE, &opt_version, N_("Print version information and exit"), NULL },
+ { G_OPTION_REMAINING, '\0', 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames, NULL, NULL },
+ { NULL }
+};
+
+
+
+gint
+main (gint argc, gchar **argv)
+{
+ MousepadApplication *application;
+ GError *error = NULL;
+ gchar *working_directory;
+#ifdef HAVE_DBUS
+ MousepadDBusService *dbus_service;
+#endif
+
+ /* bind the text domain to the locale directory */
+ bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
+
+ /* set the package textdomain */
+ textdomain (GETTEXT_PACKAGE);
+
+ /* default application name */
+ g_set_application_name (_("Mousepad"));
+
+#ifdef G_ENABLE_DEBUG
+ /* crash when something went wrong */
+ g_log_set_always_fatal (G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING);
+#endif
+
+ /* initialize the gthread system */
+ if (G_LIKELY (!g_thread_supported ()))
+ g_thread_init (NULL);
+
+ /* initialize gtk+ */
+ if (!gtk_init_with_args (&argc, &argv, _("[FILES...]"), (GOptionEntry *) option_entries, (gchar *) GETTEXT_PACKAGE, &error))
+ {
+ /* check if we have an error message */
+ if (G_LIKELY (error == NULL))
+ {
+ /* no error message, the gui initialization failed */
+ g_error ("%s", _("Failed to open display."));
+ }
+ else
+ {
+ /* print the error message */
+ g_error ("%s", error->message);
+ g_error_free (error);
+ }
+
+ return EXIT_FAILURE;
+ }
+
+ /* check if we should print version information */
+ if (G_UNLIKELY (opt_version))
+ {
+ g_print ("%s %s\n\n", PACKAGE_NAME, PACKAGE_VERSION);
+ g_print ("%s\n", "Copyright (c) 2007");
+ g_print ("\t%s\n\n", _("The Xfce development team. All rights reserved."));
+ g_print (_("Please report bugs to <%s>."), PACKAGE_BUGREPORT);
+ g_print ("\n");
+
+ return EXIT_SUCCESS;
+ }
+
+#ifdef HAVE_DBUS
+ /* check if we need to terminate a running Mousepad instance */
+ if (G_UNLIKELY (opt_quit))
+ {
+ /* try to terminate whatever is running */
+ if (!mousepad_dbus_client_terminate (&error))
+ {
+ g_error ("Failed to terminate a running instance: %s\n", error->message);
+ g_error_free (error);
+ return EXIT_FAILURE;
+ }
+
+ return EXIT_SUCCESS;
+ }
+#endif /* !HAVE_DBUS */
+
+ /* get the current working directory */
+ working_directory = g_get_current_dir ();
+
+#ifdef HAVE_DBUS
+ if (G_LIKELY (!opt_disable_server))
+ {
+ /* check if we can reuse an existing instance */
+ if (mousepad_dbus_client_launch_files (filenames, working_directory, &error))
+ {
+ /* stop any running startup notification */
+ gdk_notify_startup_complete ();
+
+ /* cleanup */
+ g_free (working_directory);
+ g_strfreev (filenames);
+
+ /* print errors, if needed */
+ if (G_UNLIKELY (error))
+ {
+ g_error ("Mousepad: %s\n", error->message);
+ g_error_free (error);
+
+ return EXIT_FAILURE;
+ }
+
+ return EXIT_SUCCESS;
+ }
+ }
+#endif /* !HAVE_DBUS */
+
+ /* use the Mousepad icon as default for new windows */
+ gtk_window_set_default_icon_name ("Mousepad");
+
+ /* create a new mousepad application */
+ application = mousepad_application_get ();
+
+ /* open an empty window (with an empty document or the files) */
+ mousepad_application_new_window_with_files (application, NULL, working_directory, filenames);
+
+ /* cleanup */
+ g_free (working_directory);
+ g_strfreev (filenames);
+
+ /* do not enter the main loop, unless we have atleast one window */
+ if (G_LIKELY (mousepad_application_has_windows (application)))
+ {
+#ifdef HAVE_DBUS
+ /* register with dbus */
+ dbus_service = g_object_new (MOUSEPAD_TYPE_DBUS_SERVICE, NULL);
+#endif
+
+ /* enter the main loop */
+ gtk_main ();
+
+#ifdef HAVE_DBUS
+ /* release dbus service reference */
+ g_object_unref (G_OBJECT (dbus_service));
+#endif
+ }
+
+ /* release application reference */
+ g_object_unref (G_OBJECT (application));
+
+ return EXIT_SUCCESS;
+}
diff --git a/mousepad/mousepad-application.c b/mousepad/mousepad-application.c
new file mode 100644
index 0000000..cb7c778
--- /dev/null
+++ b/mousepad/mousepad-application.c
@@ -0,0 +1,299 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <mousepad/mousepad-private.h>
+#include <mousepad/mousepad-application.h>
+#include <mousepad/mousepad-document.h>
+#include <mousepad/mousepad-replace-dialog.h>
+#include <mousepad/mousepad-window.h>
+
+
+
+static void mousepad_application_finalize (GObject *object);
+static void mousepad_application_window_destroyed (GtkWidget *window,
+ MousepadApplication *application);
+static GtkWidget *mousepad_application_create_window (MousepadApplication *application);
+static void mousepad_application_new_window_with_document (MousepadWindow *existing,
+ MousepadDocument *document,
+ gint x,
+ gint y,
+ MousepadApplication *application);
+static void mousepad_application_new_window (MousepadWindow *existing,
+ MousepadApplication *application);
+
+
+
+struct _MousepadApplicationClass
+{
+ GObjectClass __parent__;
+};
+
+struct _MousepadApplication
+{
+ GObject __parent__;
+
+ /* internal list of all the opened windows */
+ GSList *windows;
+};
+
+
+
+G_DEFINE_TYPE (MousepadApplication, mousepad_application, G_TYPE_OBJECT);
+
+
+
+static void
+mousepad_application_class_init (MousepadApplicationClass *klass)
+{
+ GObjectClass *gobject_class;
+
+ gobject_class = G_OBJECT_CLASS (klass);
+ gobject_class->finalize = mousepad_application_finalize;
+}
+
+
+
+static void
+mousepad_application_init (MousepadApplication *application)
+{
+ gchar *filename;
+
+ /* check if we have a saved accel map */
+ filename = mousepad_util_get_save_location (MOUSEPAD_ACCELS_RELPATH, FALSE);
+ if (G_LIKELY (filename != NULL))
+ {
+ /* load the accel map */
+ gtk_accel_map_load (filename);
+
+ /* cleanup */
+ g_free (filename);
+ }
+}
+
+
+
+static void
+mousepad_application_finalize (GObject *object)
+{
+ MousepadApplication *application = MOUSEPAD_APPLICATION (object);
+ GSList *li;
+ gchar *filename;
+
+ /* flush the history items of the replace dialog
+ * this is a bit of an ugly place, but cleaning on a window close
+ * isn't a good option eighter */
+ mousepad_replace_dialog_history_clean ();
+
+ /* save the current accel map */
+ filename = mousepad_util_get_save_location (MOUSEPAD_ACCELS_RELPATH, TRUE);
+ if (G_LIKELY (filename != NULL))
+ {
+ /* save the accel map */
+ gtk_accel_map_save (filename);
+
+ /* cleanup */
+ g_free (filename);
+ }
+
+ /* destroy the windows if they are still opened */
+ for (li = application->windows; li != NULL; li = li->next)
+ {
+ g_signal_handlers_disconnect_by_func (G_OBJECT (li->data), G_CALLBACK (mousepad_application_window_destroyed), application);
+ gtk_widget_destroy (GTK_WIDGET (li->data));
+ }
+
+ /* cleanup the list of windows */
+ g_slist_free (application->windows);
+
+ (*G_OBJECT_CLASS (mousepad_application_parent_class)->finalize) (object);
+}
+
+
+
+MousepadApplication*
+mousepad_application_get (void)
+{
+ static MousepadApplication *application = NULL;
+
+ if (G_UNLIKELY (application == NULL))
+ {
+ application = g_object_new (MOUSEPAD_TYPE_APPLICATION, NULL);
+ g_object_add_weak_pointer (G_OBJECT (application), (gpointer) &application);
+ }
+ else
+ {
+ g_object_ref (G_OBJECT (application));
+ }
+
+ return application;
+}
+
+
+
+gboolean
+mousepad_application_has_windows (MousepadApplication *application)
+{
+ mousepad_return_val_if_fail (MOUSEPAD_IS_APPLICATION (application), FALSE);
+
+ return (application->windows != NULL);
+}
+
+
+
+static void
+mousepad_application_window_destroyed (GtkWidget *window,
+ MousepadApplication *application)
+{
+ mousepad_return_if_fail (GTK_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_APPLICATION (application));
+ mousepad_return_if_fail (g_slist_find (application->windows, window) != NULL);
+
+ /* remove the window from the list */
+ application->windows = g_slist_remove (application->windows, window);
+
+ /* quit if there are no windows opened */
+ if (application->windows == NULL)
+ gtk_main_quit ();
+}
+
+
+
+void
+mousepad_application_take_window (MousepadApplication *application,
+ GtkWindow *window)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_APPLICATION (application));
+ mousepad_return_if_fail (g_slist_find (application->windows, window) == NULL);
+
+ /* connect to the "destroy" signal */
+ g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (mousepad_application_window_destroyed), application);
+
+ /* add the window to our internal list */
+ application->windows = g_slist_prepend (application->windows, window);
+}
+
+
+
+static GtkWidget *
+mousepad_application_create_window (MousepadApplication *application)
+{
+ GtkWidget *window;
+
+ /* create a new window */
+ window = mousepad_window_new ();
+
+ /* hook up the new window */
+ mousepad_application_take_window (application, GTK_WINDOW (window));
+
+ /* connect signals */
+ g_signal_connect (G_OBJECT (window), "new-window-with-document", G_CALLBACK (mousepad_application_new_window_with_document), application);
+ g_signal_connect (G_OBJECT (window), "new-window", G_CALLBACK (mousepad_application_new_window), application);
+
+ return window;
+}
+
+
+
+static void
+mousepad_application_new_window_with_document (MousepadWindow *existing,
+ MousepadDocument *document,
+ gint x,
+ gint y,
+ MousepadApplication *application)
+{
+ GtkWidget *window;
+ GdkScreen *screen;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (existing));
+ mousepad_return_if_fail (document == NULL || MOUSEPAD_IS_DOCUMENT (document));
+ mousepad_return_if_fail (MOUSEPAD_IS_APPLICATION (application));
+
+ /* create a new window (signals added and already hooked up) */
+ window = mousepad_application_create_window (application);
+
+ /* place the new window on the same screen as the existing window */
+ screen = gtk_window_get_screen (GTK_WINDOW (existing));
+ if (G_LIKELY (screen != NULL))
+ gtk_window_set_screen (GTK_WINDOW (window), screen);
+
+ /* move the window on valid cooridinates */
+ if (x > -1 && y > -1)
+ gtk_window_move (GTK_WINDOW (window), x, y);
+
+ /* create an empty document if no document was send */
+ if (document == NULL)
+ document = mousepad_document_new ();
+
+ /* add the document to the new window */
+ mousepad_window_add (MOUSEPAD_WINDOW (window), document);
+
+ /* show the window */
+ gtk_widget_show (window);
+}
+
+
+
+static void
+mousepad_application_new_window (MousepadWindow *existing,
+ MousepadApplication *application)
+{
+ /* trigger new document function */
+ mousepad_application_new_window_with_document (existing, NULL, -1, -1, application);
+}
+
+
+
+void
+mousepad_application_new_window_with_files (MousepadApplication *application,
+ GdkScreen *screen,
+ const gchar *working_directory,
+ gchar **filenames)
+{
+ GtkWidget *window;
+ gboolean succeed = FALSE;
+ MousepadDocument *document;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_APPLICATION (application));
+ mousepad_return_if_fail (screen == NULL || GDK_IS_SCREEN (screen));
+
+ /* create a new window (signals added and already hooked up) */
+ window = mousepad_application_create_window (application);
+
+ /* place the window on the right screen */
+ gtk_window_set_screen (GTK_WINDOW (window), screen ? screen : gdk_screen_get_default ());
+
+ /* try to open the files */
+ if (working_directory && filenames && g_strv_length (filenames))
+ succeed = mousepad_window_open_files (MOUSEPAD_WINDOW (window), working_directory, filenames);
+
+ /* open an empty document */
+ if (succeed == FALSE)
+ {
+ /* create a new document */
+ document = mousepad_document_new ();
+
+ /* add the document to the new window */
+ mousepad_window_add (MOUSEPAD_WINDOW (window), document);
+ }
+
+ /* show the window */
+ gtk_widget_show (window);
+}
diff --git a/mousepad/mousepad-application.h b/mousepad/mousepad-application.h
new file mode 100644
index 0000000..28e382c
--- /dev/null
+++ b/mousepad/mousepad-application.h
@@ -0,0 +1,48 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __MOUSEPAD_APPLICATION_H__
+#define __MOUSEPAD_APPLICATION_H__
+
+G_BEGIN_DECLS
+
+typedef struct _MousepadApplicationClass MousepadApplicationClass;
+typedef struct _MousepadApplication MousepadApplication;
+
+#define MOUSEPAD_TYPE_APPLICATION (mousepad_application_get_type ())
+#define MOUSEPAD_APPLICATION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MOUSEPAD_TYPE_APPLICATION, MousepadApplication))
+#define MOUSEPAD_APPLICATION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MOUSEPAD_TYPE_APPLICATION, MousepadApplicationClass))
+#define MOUSEPAD_IS_APPLICATION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MOUSEPAD_TYPE_APPLICATION))
+#define MOUSEPAD_IS_APPLICATION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MOUSEPAD_TYPE_APPLICATION))
+#define MOUSEPAD_APPLICATION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MOUSEPAD_TYPE_APPLICATION, MousepadApplicationClass))
+
+GType mousepad_application_get_type (void) G_GNUC_CONST;
+
+MousepadApplication *mousepad_application_get (void);
+
+gboolean mousepad_application_has_windows (MousepadApplication *application);
+
+void mousepad_application_take_window (MousepadApplication *application,
+ GtkWindow *window);
+
+void mousepad_application_new_window_with_files (MousepadApplication *application,
+ GdkScreen *screen,
+ const gchar *working_directory,
+ gchar **filenames);
+
+G_END_DECLS
+
+#endif /* !__MOUSEPAD_APPLICATION_H__ */
diff --git a/mousepad/mousepad-dbus-infos.xml b/mousepad/mousepad-dbus-infos.xml
new file mode 100644
index 0000000..3f0fab1
--- /dev/null
+++ b/mousepad/mousepad-dbus-infos.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ Place, Suite 330, Boston, MA 02111-1307 USA
+-->
+
+<node name="/org/xfce/Mousepad">
+
+ <!--
+ org.xfce.Mousepad
+
+ The Mousepad specific interface, which provides Mousepad specific methods.
+
+ This inferface is internally used by Mousepad and should not be used externally.
+ -->
+ <interface name="org.xfce.Mousepad">
+ <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="mousepad_dbus_service" />
+
+ <!--
+ LaunchFiles (working-directory : STRING, filenames : ARRAY OF STRING : VOID
+
+ working-directory : the directory, relative to which filenames should
+ be interpreted.
+ filenames : an array of file names to launch. The file names may
+ be either file:-URIs, absolute paths or paths relative
+ to the working-directory.
+ -->
+ <method name="LaunchFiles">
+ <arg direction="in" name="working-directory" type="s" />
+ <arg direction="in" name="filenames" type="as" />
+ </method>
+
+ <!--
+ Terminate () : VOID
+
+ Tells a running Mousepad instance to terminate immediately.
+ -->
+ <method name="Terminate">
+ </method>
+
+ </interface>
+
+</node>
diff --git a/mousepad/mousepad-dbus.c b/mousepad/mousepad-dbus.c
new file mode 100644
index 0000000..8b7e769
--- /dev/null
+++ b/mousepad/mousepad-dbus.c
@@ -0,0 +1,337 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <dbus/dbus-glib-lowlevel.h>
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus.h>
+
+#include <mousepad/mousepad-private.h>
+#include <mousepad/mousepad-dbus.h>
+#include <mousepad/mousepad-application.h>
+
+
+
+#define MOUSEPAD_DBUS_PATH "/org/xfce/Mousepad"
+#define MOUSEPAD_DBUS_INTERFACE "org.xfce.Mousepad"
+
+
+
+static void mousepad_dbus_service_finalize (GObject *object);
+static gboolean mousepad_dbus_service_launch_files (MousepadDBusService *dbus_service,
+ const gchar *working_directory,
+ gchar **filenames,
+ GError **error);
+static gboolean mousepad_dbus_service_terminate (MousepadDBusService *dbus_service,
+ GError **error);
+
+
+
+/* include the dbus glue generated by dbus-binding-tool */
+#include <mousepad/mousepad-dbus-infos.h>
+
+
+
+struct _MousepadDBusServiceClass
+{
+ GObjectClass __parent__;
+};
+
+struct _MousepadDBusService
+{
+ GObject __parent__;
+
+ DBusGConnection *connection;
+};
+
+
+
+G_DEFINE_TYPE (MousepadDBusService, mousepad_dbus_service, G_TYPE_OBJECT);
+
+
+
+static void
+mousepad_dbus_service_class_init (MousepadDBusServiceClass *klass)
+{
+ GObjectClass *gobject_class;
+
+ gobject_class = G_OBJECT_CLASS (klass);
+ gobject_class->finalize = mousepad_dbus_service_finalize;
+
+ /* install the D-BUS info for our class */
+ dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (klass), &dbus_glib_mousepad_dbus_service_object_info);
+}
+
+
+
+static void
+mousepad_dbus_service_init (MousepadDBusService *dbus_service)
+{
+ GError *error = NULL;
+
+ /* try to connect to the session bus */
+ dbus_service->connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
+
+ if (G_LIKELY (dbus_service->connection != NULL))
+ {
+ /* register the /org/xfce/TextEditor object for Mousepad */
+ dbus_g_connection_register_g_object (dbus_service->connection, MOUSEPAD_DBUS_PATH, G_OBJECT (dbus_service));
+
+ /* request the org.xfce.Mousepad name for Mousepad */
+ dbus_bus_request_name (dbus_g_connection_get_connection (dbus_service->connection),
+ MOUSEPAD_DBUS_INTERFACE, DBUS_NAME_FLAG_REPLACE_EXISTING, NULL);
+ }
+ else
+ {
+#ifdef NDEBUG
+ /* hide this warning when the user is root and debug is disabled */
+ if (geteuid () != 0)
+#endif
+ {
+ /* notify the user that D-BUS service won't be available */
+ g_message ("Failed to connect to the D-BUS session bus: %s\n", error->message);
+ }
+
+ g_error_free (error);
+ }
+}
+
+
+
+static void
+mousepad_dbus_service_finalize (GObject *object)
+{
+ MousepadDBusService *dbus_service = MOUSEPAD_DBUS_SERVICE (object);
+
+ /* release the D-BUS connection object */
+ if (G_LIKELY (dbus_service->connection != NULL))
+ dbus_g_connection_unref (dbus_service->connection);
+
+ (*G_OBJECT_CLASS (mousepad_dbus_service_parent_class)->finalize) (object);
+}
+
+
+
+/**
+ * mousepad_dbus_service_launch_files:
+ * @dbus_service : A #MousepadDBusService.
+ * @working_directory : The default working directory for this window.
+ * @filenames : A list of filenames we try to open in tabs. The file names
+ * can either be absolute paths, supported URIs or relative file
+ * names to @working_directory or %NULL for an untitled document.
+ * @error : Return location for errors, not used atm.
+ *
+ * This function is activated by DBus (service) and opens a new window in this instance of
+ * Mousepad.
+ *
+ * Return value: %TRUE on success, %FALSE if @error is set.
+ **/
+static gboolean
+mousepad_dbus_service_launch_files (MousepadDBusService *dbus_service,
+ const gchar *working_directory,
+ gchar **filenames,
+ GError **error)
+{
+ MousepadApplication *application;
+
+ mousepad_return_val_if_fail (g_path_is_absolute (working_directory), FALSE);
+ mousepad_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ /* open a mousepad window */
+ application = mousepad_application_get ();
+ mousepad_application_new_window_with_files (application, NULL, working_directory, filenames);
+ g_object_unref (G_OBJECT (application));
+
+ return TRUE;
+}
+
+
+
+/**
+ * mousepad_dbus_service_terminate:
+ * @dbus_service : A #MousepadDBusService.
+ * @error : Return location for errors, not used atm.
+ *
+ * This function quits this instance of Mousepad.
+ *
+ * Return value: %TRUE on success.
+ **/
+static gboolean
+mousepad_dbus_service_terminate (MousepadDBusService *dbus_service,
+ GError **error)
+{
+ /* leave the Gtk main loop as soon as possible */
+ gtk_main_quit ();
+
+ /* we cannot fail */
+ return TRUE;
+}
+
+
+
+/**
+ * mousepad_dbus_client_send:
+ * @message : A #DBusMessage.
+ * @error : Return location for errors or %NULL.
+ *
+ * This function sends the DBus message and should avoid
+ * code duplication in the functions below.
+ *
+ * Return value: %TRUE on succeed or %FALSE if @error is set.
+ **/
+static gboolean
+mousepad_dbus_client_send (DBusMessage *message,
+ GError **error)
+{
+ DBusConnection *connection;
+ DBusMessage *result;
+ DBusError derror;
+
+ dbus_error_init (&derror);
+
+ /* try to connect to the session bus */
+ connection = dbus_bus_get (DBUS_BUS_SESSION, &derror);
+ if (G_UNLIKELY (connection == NULL))
+ {
+ dbus_set_g_error (error, &derror);
+ dbus_error_free (&derror);
+ return FALSE;
+ }
+
+ /* send the message */
+ result = dbus_connection_send_with_reply_and_block (connection, message, -1, &derror);
+
+ /* check if no reply was received */
+ if (result == NULL)
+ {
+ /* check if there was just no instance running */
+ if (!dbus_error_has_name (&derror, DBUS_ERROR_NAME_HAS_NO_OWNER))
+ dbus_set_g_error (error, &derror);
+
+ dbus_error_free (&derror);
+ return FALSE;
+ }
+
+ /* but maybe we received an error */
+ if (G_UNLIKELY (dbus_message_get_type (result) == DBUS_MESSAGE_TYPE_ERROR))
+ {
+ dbus_set_error_from_message (&derror, result);
+ dbus_set_g_error (error, &derror);
+ dbus_message_unref (result);
+ dbus_error_free (&derror);
+ return FALSE;
+ }
+
+ /* it seems everything worked */
+ dbus_message_unref (result);
+
+ return TRUE;
+}
+
+
+
+/**
+ * mousepad_dbus_client_terminate:
+ * @error : Return location for errors or %NULL.
+ *
+ * Function called from this instance of the application and tries to invoke
+ * with an already running instance and ties to quit it.
+ * The mousepad_dbus_service_terminate function is activated in the running instance.
+ *
+ * Return value: %TRUE on success.
+ **/
+gboolean
+mousepad_dbus_client_terminate (GError **error)
+{
+ DBusMessage *message;
+
+ mousepad_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ /* generate the message */
+ message = dbus_message_new_method_call (MOUSEPAD_DBUS_INTERFACE, MOUSEPAD_DBUS_PATH,
+ MOUSEPAD_DBUS_INTERFACE, "Terminate");
+ dbus_message_set_auto_start (message, FALSE);
+
+
+ /* send the message */
+ mousepad_dbus_client_send (message, error);
+
+ /* unref the message */
+ dbus_message_unref (message);
+
+ /* we return false if an error was set */
+ return (error != NULL);
+}
+
+
+
+/**
+ * mousepad_dbus_client_launch_files:
+ * @filenames : A list of filenames we try to open in tabs. The file names
+ * can either be absolute paths, supported URIs or relative file
+ * names to @working_directory or %NULL for an untitled document.
+ * @working_directory : Working directory for the new Mousepad window.
+ * @error : Return location for errors or %NULL.
+ *
+ * This function is called within this instance and tries to connect a running instance
+ * of Mousepad via DBus. The function mousepad_dbus_service_launch_files is activated in the
+ * running instance.
+ *
+ * Return value: %TRUE on success.
+ **/
+gboolean
+mousepad_dbus_client_launch_files (gchar **filenames,
+ const gchar *working_directory,
+ GError **error)
+{
+ DBusMessage *message;
+ guint length = 0;
+ gboolean succeed;
+
+ mousepad_return_val_if_fail (g_path_is_absolute (working_directory), FALSE);
+ mousepad_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ /* get the length of the filesname string */
+ if (filenames)
+ length = g_strv_length (filenames);
+
+ /* generate the message */
+ message = dbus_message_new_method_call (MOUSEPAD_DBUS_INTERFACE, MOUSEPAD_DBUS_PATH,
+ MOUSEPAD_DBUS_INTERFACE, "LaunchFiles");
+ dbus_message_set_auto_start (message, FALSE);
+ dbus_message_append_args (message,
+ DBUS_TYPE_STRING, &working_directory,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &filenames, length,
+ DBUS_TYPE_INVALID);
+
+ /* send the message */
+ succeed = mousepad_dbus_client_send (message, error);
+
+ /* unref the message */
+ dbus_message_unref (message);
+
+ return succeed;
+}
diff --git a/mousepad/mousepad-dbus.h b/mousepad/mousepad-dbus.h
new file mode 100644
index 0000000..e38ec44
--- /dev/null
+++ b/mousepad/mousepad-dbus.h
@@ -0,0 +1,40 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __MOUSEPAD_DBUS_H__
+#define __MOUSEPAD_DBUS_H__
+
+typedef struct _MousepadDBusServiceClass MousepadDBusServiceClass;
+typedef struct _MousepadDBusService MousepadDBusService;
+
+#define MOUSEPAD_TYPE_DBUS_SERVICE (mousepad_dbus_service_get_type ())
+#define MOUSEPAD_DBUS_SERVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MOUSEPAD_TYPE_DBUS_SERVICE, MousepadDBusService))
+#define MOUSEPAD_DBUS_SERVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MOUSEPAD_TYPE_DBUS_SERVICE, MousepadDBusServiceClass))
+#define MOUSEPAD_IS_DBUS_SERVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MOUSEPAD_TYPE_DBUS_SERVICE))
+#define MOUSEPAD_IS_DBUS_SERVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MOUSEPAD_TYPE_DBUS_BRIGDE))
+#define MOUSEPAD_DBUS_SERVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MOUSEPAD_TYPE_DBUS_SERVICE, MousepadDBusServiceClass))
+
+GType mousepad_dbus_service_get_type (void) G_GNUC_CONST;
+
+gboolean mousepad_dbus_client_terminate (GError **error);
+
+gboolean mousepad_dbus_client_launch_files (gchar **filenames,
+ const gchar *working_directory,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* !__MOUSEPAD_DBUS_H__ */
diff --git a/mousepad/mousepad-dialogs.c b/mousepad/mousepad-dialogs.c
new file mode 100644
index 0000000..28271d2
--- /dev/null
+++ b/mousepad/mousepad-dialogs.c
@@ -0,0 +1,453 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <mousepad/mousepad-private.h>
+#include <mousepad/mousepad-dialogs.h>
+#include <mousepad/mousepad-util.h>
+
+
+
+void
+mousepad_dialogs_show_about (GtkWindow *parent)
+{
+ static const gchar *authors[] =
+ {
+ "Nick Schermer <nick at xfce.org>",
+ "Erik Harrison <erikharrison at xfce.org>",
+ NULL
+ };
+
+ /* show the dialog */
+ gtk_show_about_dialog (parent,
+ "authors", authors,
+ "comments", _("Mousepad is a fast text editor for the Xfce Desktop Environment."),
+ "destroy-with-parent", TRUE,
+ "logo-icon-name", "accessories-text-editor",
+#if GTK_CHECK_VERSION (2,12,0)
+ "program-name", PACKAGE_NAME,
+#else
+ "name", PACKAGE_NAME,
+#endif
+ "version", PACKAGE_VERSION,
+ "translator-credits", _("translator-credits"),
+ "website", "http://www.xfce.org/",
+ NULL);
+}
+
+
+
+void
+mousepad_dialogs_show_error (GtkWindow *parent,
+ const GError *error,
+ const gchar *message)
+{
+ GtkWidget *dialog;
+
+ /* create the warning dialog */
+ dialog = gtk_message_dialog_new (parent,
+ GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_CLOSE,
+ "%s.", message);
+
+ /* set secondary text if an error is provided */
+ if (G_LIKELY (error != NULL))
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "%s.", error->message);
+
+ /* display the dialog */
+ gtk_dialog_run (GTK_DIALOG (dialog));
+
+ /* cleanup */
+ gtk_widget_destroy (dialog);
+}
+
+
+
+void
+mousepad_dialogs_show_help (GtkWindow *parent,
+ const gchar *page,
+ const gchar *offset)
+{
+ GdkScreen *screen;
+ GError *error = NULL;
+ gchar *command;
+ gchar *tmp;
+
+ /* get screen */
+ if (G_LIKELY (parent))
+ screen = gtk_widget_get_screen (GTK_WIDGET (parent));
+ else
+ screen = gdk_screen_get_default ();
+
+ /* generate the command for the documentation browser */
+ command = g_strdup (LIBDIR G_DIR_SEPARATOR_S "xfce4" G_DIR_SEPARATOR_S "mousepad" G_DIR_SEPARATOR_S "MousepadHelp");
+
+ /* check if a page is given */
+ if (G_UNLIKELY (page != NULL))
+ {
+ /* append page as second parameter */
+ tmp = g_strconcat (command, " ", page, NULL);
+ g_free (command);
+ command = tmp;
+
+ /* check if an offset is given */
+ if (G_UNLIKELY (offset != NULL))
+ {
+ /* append offset as third parameter */
+ tmp = g_strconcat (command, " ", offset, NULL);
+ g_free (command);
+ command = tmp;
+ }
+ }
+
+ /* try to run the documentation browser */
+ if (!gdk_spawn_command_line_on_screen (screen, command, &error))
+ {
+ /* display an error message to the user */
+ mousepad_dialogs_show_error (parent, error, _("Failed to open the documentation browser"));
+ g_error_free (error);
+ }
+
+ /* cleanup */
+ g_free (command);
+}
+
+
+
+gint
+mousepad_dialogs_other_tab_size (GtkWindow *parent,
+ gint active_size)
+{
+ GtkWidget *dialog;
+ GtkWidget *scale;
+
+ /* build dialog */
+ dialog = gtk_dialog_new_with_buttons (_("Select Tab Size"),
+ parent,
+ GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR,
+ GTK_STOCK_CANCEL, MOUSEPAD_RESPONSE_CANCEL,
+ GTK_STOCK_OK, MOUSEPAD_RESPONSE_OK,
+ NULL);
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), MOUSEPAD_RESPONSE_OK);
+
+ /* create scale widget */
+ scale = gtk_hscale_new_with_range (1, 32, 1);
+ gtk_range_set_value (GTK_RANGE (scale), active_size);
+ gtk_scale_set_digits (GTK_SCALE (scale), 0);
+ gtk_scale_set_draw_value (GTK_SCALE (scale), TRUE);
+ gtk_scale_set_value_pos (GTK_SCALE (scale), GTK_POS_TOP);
+ gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), scale, TRUE, TRUE, 0);
+ gtk_widget_show (scale);
+
+ /* run the dialog */
+ if (gtk_dialog_run (GTK_DIALOG (dialog)) == MOUSEPAD_RESPONSE_OK)
+ active_size = gtk_range_get_value (GTK_RANGE (scale));
+
+ /* destroy the dialog */
+ gtk_widget_destroy (dialog);
+
+ return active_size;
+}
+
+
+
+static void
+mousepad_dialogs_go_to_line_changed (GtkSpinButton *line_spin,
+ GtkSpinButton *col_spin)
+{
+ GtkTextBuffer *buffer;
+ GtkTextIter iter;
+
+ mousepad_return_if_fail (GTK_IS_SPIN_BUTTON (line_spin));
+ mousepad_return_if_fail (GTK_IS_SPIN_BUTTON (col_spin));
+
+ /* get the text buffer */
+ buffer = mousepad_object_get_data (G_OBJECT (col_spin), "buffer");
+
+ /* debug check */
+ mousepad_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
+
+ /* get iter at line */
+ gtk_text_buffer_get_iter_at_line (buffer, &iter, gtk_spin_button_get_value_as_int (line_spin) - 1);
+
+ /* move the iter to the end of the line if needed */
+ if (!gtk_text_iter_ends_line (&iter))
+ gtk_text_iter_forward_to_line_end (&iter);
+
+ /* update column spin button range */
+ gtk_spin_button_set_range (col_spin, 0, gtk_text_iter_get_line_offset (&iter));
+}
+
+
+
+gboolean
+mousepad_dialogs_go_to (GtkWindow *parent,
+ GtkTextBuffer *buffer)
+{
+ GtkWidget *dialog;
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+ GtkWidget *label;
+ GtkWidget *line_spin;
+ GtkWidget *col_spin;
+ GtkSizeGroup *size_group;
+ GtkTextIter iter;
+ gint line, column, lines;
+ gint response;
+
+ /* get cursor iter */
+ gtk_text_buffer_get_iter_at_mark (buffer, &iter, gtk_text_buffer_get_insert (buffer));
+ line = gtk_text_iter_get_line (&iter) + 1;
+
+ /* get number of lines */
+ lines = gtk_text_buffer_get_line_count (buffer);
+
+ /* build the dialog */
+ dialog = gtk_dialog_new_with_buttons (_("Go To"),
+ parent,
+ GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR,
+ GTK_STOCK_CANCEL, MOUSEPAD_RESPONSE_CANCEL,
+ GTK_STOCK_JUMP_TO, MOUSEPAD_RESPONSE_JUMP_TO,
+ NULL);
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), MOUSEPAD_RESPONSE_JUMP_TO);
+ gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
+
+ vbox = gtk_vbox_new (FALSE, 6);
+ gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), vbox, TRUE, TRUE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
+ gtk_widget_show (vbox);
+
+ /* create size group */
+ size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+
+ /* line number box */
+ hbox = gtk_hbox_new (FALSE, 12);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
+ gtk_widget_show (hbox);
+
+ label = gtk_label_new_with_mnemonic (_("_Line number:"));
+ gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
+ gtk_size_group_add_widget (size_group, label);
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+ gtk_widget_show (label);
+
+ line_spin = gtk_spin_button_new_with_range (1, lines, 1);
+ gtk_entry_set_activates_default (GTK_ENTRY (line_spin), TRUE);
+ gtk_box_pack_start (GTK_BOX (hbox), line_spin, FALSE, FALSE, 0);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), line_spin);
+ gtk_spin_button_set_snap_to_ticks (GTK_SPIN_BUTTON (line_spin), TRUE);
+ gtk_entry_set_width_chars (GTK_ENTRY (line_spin), 8);
+ gtk_widget_show (line_spin);
+
+ /* column box */
+ hbox = gtk_hbox_new (FALSE, 12);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
+ gtk_widget_show (hbox);
+
+ label = gtk_label_new_with_mnemonic (_("C_olumn number:"));
+ gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
+ gtk_size_group_add_widget (size_group, label);
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+ gtk_widget_show (label);
+
+ col_spin = gtk_spin_button_new_with_range (0, 0, 1);
+ gtk_entry_set_activates_default (GTK_ENTRY (col_spin), TRUE);
+ mousepad_object_set_data (G_OBJECT (col_spin), "buffer", buffer);
+ gtk_box_pack_start (GTK_BOX (hbox), col_spin, FALSE, FALSE, 0);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), col_spin);
+ gtk_spin_button_set_snap_to_ticks (GTK_SPIN_BUTTON (col_spin), TRUE);
+ gtk_entry_set_width_chars (GTK_ENTRY (col_spin), 8);
+ gtk_widget_show (col_spin);
+
+ /* signal to monitor column number */
+ g_signal_connect (G_OBJECT (line_spin), "value-changed", G_CALLBACK (mousepad_dialogs_go_to_line_changed), col_spin);
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (line_spin), line);
+
+ /* run the dialog */
+ response = gtk_dialog_run (GTK_DIALOG (dialog));
+ if (response == MOUSEPAD_RESPONSE_JUMP_TO)
+ {
+ /* hide the dialog */
+ gtk_widget_hide (dialog);
+
+ /* get new position */
+ line = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (line_spin)) - 1;
+ column = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (col_spin));
+
+ /* get iter */
+ gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, line, column);
+
+ /* get cursor position */
+ gtk_text_buffer_place_cursor (buffer, &iter);
+ }
+
+ /* release size group */
+ g_object_unref (G_OBJECT (size_group));
+
+ /* destroy the dialog */
+ gtk_widget_destroy (dialog);
+
+ return (response == MOUSEPAD_RESPONSE_JUMP_TO);
+}
+
+
+
+gboolean
+mousepad_dialogs_clear_recent (GtkWindow *parent)
+{
+ GtkWidget *dialog;
+ GtkWidget *image;
+ gboolean succeed = FALSE;
+
+ /* create the question dialog */
+ dialog = gtk_message_dialog_new (parent, GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_OTHER, GTK_BUTTONS_NONE,
+ _("Remove all entries from the documents history?"));
+ gtk_dialog_add_buttons (GTK_DIALOG (dialog),
+ GTK_STOCK_CANCEL, MOUSEPAD_RESPONSE_CANCEL,
+ GTK_STOCK_CLEAR, MOUSEPAD_RESPONSE_CLEAR,
+ NULL);
+ gtk_window_set_title (GTK_WINDOW (dialog), _("Clear Documents History"));
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), MOUSEPAD_RESPONSE_CANCEL);
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+ _("Clearing the documents history will permanently "
+ "remove all currently listed entries."));
+
+ /* the dialog icon */
+ image = gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_DIALOG);
+ gtk_message_dialog_set_image (GTK_MESSAGE_DIALOG (dialog), image);
+ gtk_widget_show (image);
+
+ /* popup the dialog */
+ if (gtk_dialog_run (GTK_DIALOG (dialog)) == MOUSEPAD_RESPONSE_CLEAR)
+ succeed = TRUE;
+
+ /* destroy the dialog */
+ gtk_widget_destroy (dialog);
+
+ return succeed;
+}
+
+
+
+gint
+mousepad_dialogs_save_changes (GtkWindow *parent,
+ gboolean readonly)
+{
+ GtkWidget *dialog;
+ GtkWidget *image;
+ gint response;
+
+ /* create the question dialog */
+ dialog = gtk_message_dialog_new (parent, GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_OTHER, GTK_BUTTONS_NONE,
+ _("Do you want to save the changes before closing?"));
+ gtk_window_set_title (GTK_WINDOW (dialog), _("Save Changes"));
+ gtk_dialog_add_action_widget (GTK_DIALOG (dialog), mousepad_util_image_button (GTK_STOCK_DELETE, _("_Don't Save")), MOUSEPAD_RESPONSE_DONT_SAVE);
+ gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_CANCEL, MOUSEPAD_RESPONSE_CANCEL);
+
+ /* we show the save as button instead of save for readonly document */
+ if (G_UNLIKELY (readonly))
+ {
+ image = gtk_image_new_from_stock (GTK_STOCK_SAVE_AS, GTK_ICON_SIZE_DIALOG);
+ gtk_dialog_add_buttons (GTK_DIALOG (dialog), GTK_STOCK_SAVE_AS, MOUSEPAD_RESPONSE_SAVE_AS, NULL);
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), MOUSEPAD_RESPONSE_SAVE_AS);
+ }
+ else
+ {
+ image = gtk_image_new_from_stock (GTK_STOCK_SAVE, GTK_ICON_SIZE_DIALOG);
+ gtk_dialog_add_buttons (GTK_DIALOG (dialog), GTK_STOCK_SAVE, MOUSEPAD_RESPONSE_SAVE, NULL);
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), MOUSEPAD_RESPONSE_SAVE);
+ }
+
+ /* the dialog icon */
+ gtk_message_dialog_set_image (GTK_MESSAGE_DIALOG (dialog), image);
+ gtk_widget_show (image);
+
+ /* secondary text */
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), _("If you don't save the document, all the changes will be lost."));
+
+ /* run the dialog and wait for a response */
+ response = gtk_dialog_run (GTK_DIALOG (dialog));
+
+ /* destroy the dialog */
+ gtk_widget_destroy (dialog);
+
+ return response;
+}
+
+
+
+gint
+mousepad_dialogs_externally_modified (GtkWindow *parent)
+{
+ GtkWidget *dialog;
+ gint response;
+
+ /* create the question dialog */
+ dialog = gtk_message_dialog_new (parent, GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_WARNING, GTK_BUTTONS_NONE,
+ _("The document has been externally modified. Do you want to continue saving?"));
+ gtk_window_set_title (GTK_WINDOW (dialog), _("Externally Modified"));
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), _("If you don't save the document, all the external changes will be lost."));
+ gtk_dialog_add_buttons (GTK_DIALOG (dialog),
+ GTK_STOCK_CANCEL, MOUSEPAD_RESPONSE_CANCEL,
+ GTK_STOCK_SAVE_AS, MOUSEPAD_RESPONSE_SAVE_AS,
+ GTK_STOCK_SAVE, MOUSEPAD_RESPONSE_SAVE, NULL);
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), MOUSEPAD_RESPONSE_CANCEL);
+
+ /* run the dialog */
+ response = gtk_dialog_run (GTK_DIALOG (dialog));
+
+ /* destroy the dialog */
+ gtk_widget_destroy (dialog);
+
+ return response;
+}
+
+
+
+gint
+mousepad_dialogs_revert (GtkWindow *parent)
+{
+ GtkWidget *dialog;
+ gint response;
+
+ /* setup the question dialog */
+ dialog = gtk_message_dialog_new (parent,
+ GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
+ _("Do you want to save your changes before reloading?"));
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+ _("If you revert the file, all unsaved changes will be lost."));
+ gtk_dialog_add_buttons (GTK_DIALOG (dialog),
+ GTK_STOCK_CANCEL, MOUSEPAD_RESPONSE_CANCEL,
+ GTK_STOCK_SAVE_AS, MOUSEPAD_RESPONSE_SAVE_AS,
+ GTK_STOCK_REVERT_TO_SAVED, MOUSEPAD_RESPONSE_REVERT,
+ NULL);
+
+ /* run the dialog */
+ response = gtk_dialog_run (GTK_DIALOG (dialog));
+
+ /* destroy the dialog */
+ gtk_widget_destroy (dialog);
+
+ return response;
+}
diff --git a/mousepad/mousepad-dialogs.h b/mousepad/mousepad-dialogs.h
new file mode 100644
index 0000000..cb01fd0
--- /dev/null
+++ b/mousepad/mousepad-dialogs.h
@@ -0,0 +1,69 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __MOUSEPAD_DIALOGS_H__
+#define __MOUSEPAD_DIALOGS_H__
+
+G_BEGIN_DECLS
+
+/* dialog responses */
+enum {
+ MOUSEPAD_RESPONSE_CANCEL,
+ MOUSEPAD_RESPONSE_CLEAR,
+ MOUSEPAD_RESPONSE_CLOSE,
+ MOUSEPAD_RESPONSE_DONT_SAVE,
+ MOUSEPAD_RESPONSE_FIND,
+ MOUSEPAD_RESPONSE_JUMP_TO,
+ MOUSEPAD_RESPONSE_OK,
+ MOUSEPAD_RESPONSE_OVERWRITE,
+ MOUSEPAD_RESPONSE_REPLACE,
+ MOUSEPAD_RESPONSE_REVERT,
+ MOUSEPAD_RESPONSE_SAVE,
+ MOUSEPAD_RESPONSE_SAVE_AS,
+ MOUSEPAD_RESPONSE_CHECK_ENTRY
+};
+
+GtkWidget *mousepad_dialogs_image_button (const gchar *stock_id,
+ const gchar *label);
+
+void mousepad_dialogs_show_about (GtkWindow *parent);
+
+void mousepad_dialogs_show_error (GtkWindow *parent,
+ const GError *error,
+ const gchar *message);
+
+void mousepad_dialogs_show_help (GtkWindow *parent,
+ const gchar *page,
+ const gchar *offset);
+
+gint mousepad_dialogs_other_tab_size (GtkWindow *parent,
+ gint active_size);
+
+gboolean mousepad_dialogs_go_to (GtkWindow *parent,
+ GtkTextBuffer *buffer);
+
+gboolean mousepad_dialogs_clear_recent (GtkWindow *parent);
+
+gint mousepad_dialogs_save_changes (GtkWindow *parent,
+ gboolean readonly);
+
+gint mousepad_dialogs_externally_modified (GtkWindow *parent);
+
+gint mousepad_dialogs_revert (GtkWindow *parent);
+
+G_END_DECLS
+
+#endif /* !__MOUSEPAD_DIALOGS_H__ */
diff --git a/mousepad/mousepad-document.c b/mousepad/mousepad-document.c
new file mode 100644
index 0000000..a3d9099
--- /dev/null
+++ b/mousepad/mousepad-document.c
@@ -0,0 +1,649 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_MEMORY_H
+#include <memory.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
+
+#include <mousepad/mousepad-private.h>
+#include <mousepad/mousepad-util.h>
+#include <mousepad/mousepad-document.h>
+#include <mousepad/mousepad-marshal.h>
+#include <mousepad/mousepad-view.h>
+#include <mousepad/mousepad-preferences.h>
+#include <mousepad/mousepad-window.h>
+
+
+
+#define MOUSEPAD_DOCUMENT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), MOUSEPAD_TYPE_DOCUMENT, MousepadDocumentPrivate))
+
+
+
+static void mousepad_document_finalize (GObject *object);
+static void mousepad_document_notify_cursor_position (GtkTextBuffer *buffer,
+ GParamSpec *pspec,
+ MousepadDocument *document);
+static void mousepad_document_notify_has_selection (GtkTextBuffer *buffer,
+ GParamSpec *pspec,
+ MousepadDocument *document);
+static void mousepad_document_notify_overwrite (GtkTextView *textview,
+ GParamSpec *pspec,
+ MousepadDocument *document);
+static void mousepad_document_notify_language (GtkSourceBuffer *buffer,
+ GParamSpec *pspec,
+ MousepadDocument *document);
+static void mousepad_document_drag_data_received (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ GtkSelectionData *selection_data,
+ guint info,
+ guint drag_time,
+ MousepadDocument *document);
+static void mousepad_document_filename_changed (MousepadDocument *document,
+ const gchar *filename);
+static void mousepad_document_label_color (MousepadDocument *document);
+static void mousepad_document_tab_button_clicked (GtkWidget *widget,
+ MousepadDocument *document);
+
+
+enum
+{
+ CLOSE_TAB,
+ CURSOR_CHANGED,
+ SELECTION_CHANGED,
+ OVERWRITE_CHANGED,
+ LANGUAGE_CHANGED,
+ LAST_SIGNAL
+};
+
+struct _MousepadDocumentClass
+{
+ GtkScrolledWindowClass __parent__;
+};
+
+struct _MousepadDocumentPrivate
+{
+ GtkScrolledWindow __parent__;
+
+ /* the tab label and ebox */
+ GtkWidget *ebox;
+ GtkWidget *label;
+
+ /* utf-8 valid document names */
+ gchar *utf8_filename;
+ gchar *utf8_basename;
+
+ /* settings */
+ guint word_wrap : 1;
+};
+
+
+
+static guint document_signals[LAST_SIGNAL];
+
+
+
+MousepadDocument *
+mousepad_document_new (void)
+{
+ return g_object_new (MOUSEPAD_TYPE_DOCUMENT, NULL);
+}
+
+
+
+G_DEFINE_TYPE (MousepadDocument, mousepad_document, GTK_TYPE_SCROLLED_WINDOW);
+
+
+
+static void
+mousepad_document_class_init (MousepadDocumentClass *klass)
+{
+ GObjectClass *gobject_class;
+
+ g_type_class_add_private (klass, sizeof (MousepadDocumentPrivate));
+
+ gobject_class = G_OBJECT_CLASS (klass);
+ gobject_class->finalize = mousepad_document_finalize;
+
+ document_signals[CLOSE_TAB] =
+ g_signal_new (I_("close-tab"),
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ document_signals[CURSOR_CHANGED] =
+ g_signal_new (I_("cursor-changed"),
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL,
+ _mousepad_marshal_VOID__INT_INT_INT,
+ G_TYPE_NONE, 3, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT);
+
+ document_signals[SELECTION_CHANGED] =
+ g_signal_new (I_("selection-changed"),
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL,
+ g_cclosure_marshal_VOID__INT,
+ G_TYPE_NONE, 1, G_TYPE_INT);
+
+ document_signals[OVERWRITE_CHANGED] =
+ g_signal_new (I_("overwrite-changed"),
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL,
+ g_cclosure_marshal_VOID__BOOLEAN,
+ G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
+
+ document_signals[LANGUAGE_CHANGED] =
+ g_signal_new (I_("language-changed"),
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1, GTK_TYPE_SOURCE_LANGUAGE);
+}
+
+
+
+static void
+mousepad_document_init (MousepadDocument *document)
+{
+ GtkTargetList *target_list;
+ gboolean word_wrap, auto_indent, line_numbers, insert_spaces;
+ gchar *font_name, *color_scheme;
+ gint tab_size;
+ GtkSourceStyleScheme *scheme = NULL;
+ MousepadPreferences *preferences;
+
+ /* private structure */
+ document->priv = MOUSEPAD_DOCUMENT_GET_PRIVATE (document);
+
+ /* initialize the variables */
+ document->priv->utf8_filename = NULL;
+ document->priv->utf8_basename = NULL;
+ document->priv->label = NULL;
+
+ /* setup the scolled window */
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (document), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (document), GTK_SHADOW_ETCHED_IN);
+ gtk_scrolled_window_set_hadjustment (GTK_SCROLLED_WINDOW (document), NULL);
+ gtk_scrolled_window_set_vadjustment (GTK_SCROLLED_WINDOW (document), NULL);
+
+ /* create a textbuffer */
+ document->buffer = GTK_TEXT_BUFFER (gtk_source_buffer_new (NULL));
+
+ /* initialize the file */
+ document->file = mousepad_file_new (document->buffer);
+
+ /* connect signals to the file */
+ g_signal_connect_swapped (G_OBJECT (document->file), "filename-changed", G_CALLBACK (mousepad_document_filename_changed), document);
+
+ /* create the highlight tag */
+ document->tag = gtk_text_buffer_create_tag (document->buffer, NULL, "background", "#ffff78", NULL);
+
+ /* setup the textview */
+ document->textview = g_object_new (MOUSEPAD_TYPE_VIEW, "buffer", document->buffer, NULL);
+ gtk_container_add (GTK_CONTAINER (document), GTK_WIDGET (document->textview));
+ gtk_widget_show (GTK_WIDGET (document->textview));
+
+ /* also allow dropping of uris and tabs in the textview */
+ target_list = gtk_drag_dest_get_target_list (GTK_WIDGET (document->textview));
+ gtk_target_list_add_table (target_list, drop_targets, G_N_ELEMENTS (drop_targets));
+
+ /* preferences */
+ preferences = mousepad_preferences_get ();
+
+ /* read all the default settings */
+ g_object_get (G_OBJECT (preferences),
+ "view-word-wrap", &word_wrap,
+ "view-line-numbers", &line_numbers,
+ "view-auto-indent", &auto_indent,
+ "view-font-name", &font_name,
+ "view-tab-size", &tab_size,
+ "view-insert-spaces", &insert_spaces,
+ "view-color-scheme", &color_scheme,
+ NULL);
+
+ /* release the preferences */
+ g_object_unref (G_OBJECT (preferences));
+
+ /* set all the settings */
+ mousepad_document_set_word_wrap (document, word_wrap);
+ mousepad_document_set_font (document, font_name);
+ mousepad_view_set_line_numbers (document->textview, line_numbers);
+ mousepad_view_set_auto_indent (document->textview, auto_indent);
+ mousepad_view_set_tab_size (document->textview, tab_size);
+ mousepad_view_set_insert_spaces (document->textview, insert_spaces);
+
+ if (g_strcmp0 (color_scheme, "none") != 0)
+ scheme = gtk_source_style_scheme_manager_get_scheme (gtk_source_style_scheme_manager_get_default (), color_scheme);
+ gtk_source_buffer_set_highlight_syntax (GTK_SOURCE_BUFFER (document->buffer), (scheme != NULL));
+ gtk_source_buffer_set_style_scheme (GTK_SOURCE_BUFFER (document->buffer), scheme);
+
+ /* cleanup */
+ g_free (font_name);
+ g_free (color_scheme);
+
+ /* attach signals to the text view and buffer */
+ g_signal_connect (G_OBJECT (document->buffer), "notify::cursor-position", G_CALLBACK (mousepad_document_notify_cursor_position), document);
+ g_signal_connect (G_OBJECT (document->buffer), "notify::has-selection", G_CALLBACK (mousepad_document_notify_has_selection), document);
+ g_signal_connect_swapped (G_OBJECT (document->buffer), "modified-changed", G_CALLBACK (mousepad_document_label_color), document);
+ g_signal_connect_swapped (G_OBJECT (document->file), "readonly-changed", G_CALLBACK (mousepad_document_label_color), document);
+ g_signal_connect (G_OBJECT (document->textview), "notify::overwrite", G_CALLBACK (mousepad_document_notify_overwrite), document);
+ g_signal_connect (G_OBJECT (document->textview), "drag-data-received", G_CALLBACK (mousepad_document_drag_data_received), document);
+ g_signal_connect (G_OBJECT (document->buffer), "notify::language", G_CALLBACK (mousepad_document_notify_language), document);
+}
+
+
+
+static void
+mousepad_document_finalize (GObject *object)
+{
+ MousepadDocument *document = MOUSEPAD_DOCUMENT (object);
+
+ /* cleanup */
+ g_free (document->priv->utf8_filename);
+ g_free (document->priv->utf8_basename);
+
+ /* release the file */
+ g_object_unref (G_OBJECT (document->file));
+
+ /* release the buffer reference */
+ g_object_unref (G_OBJECT (document->buffer));
+
+ (*G_OBJECT_CLASS (mousepad_document_parent_class)->finalize) (object);
+}
+
+
+
+static void
+mousepad_document_notify_cursor_position (GtkTextBuffer *buffer,
+ GParamSpec *pspec,
+ MousepadDocument *document)
+{
+ GtkTextIter iter;
+ gint line, column, selection;
+ gint tab_size;
+
+ mousepad_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (document));
+
+ /* get the current iter position */
+ gtk_text_buffer_get_iter_at_mark (buffer, &iter, gtk_text_buffer_get_insert (buffer));
+
+ /* get the current line number */
+ line = gtk_text_iter_get_line (&iter) + 1;
+
+ /* get the tab size */
+ tab_size = mousepad_view_get_tab_size (document->textview);
+
+ /* get the column */
+ column = mousepad_util_get_real_line_offset (&iter, tab_size);
+
+ /* get length of the selection */
+ selection = mousepad_view_get_selection_length (document->textview, NULL);
+
+ /* emit the signal */
+ g_signal_emit (G_OBJECT (document), document_signals[CURSOR_CHANGED], 0, line, column, selection);
+}
+
+
+
+static void
+mousepad_document_notify_has_selection (GtkTextBuffer *buffer,
+ GParamSpec *pspec,
+ MousepadDocument *document)
+{
+ gint selection;
+ gboolean is_column_selection;
+
+ mousepad_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (document));
+
+ /* get length of the selection */
+ selection = mousepad_view_get_selection_length (document->textview, &is_column_selection);
+
+ /* don't send large numbers */
+ if (selection > 1)
+ selection = 1;
+
+ /* if it's a column selection with content */
+ if (selection == 1 && is_column_selection)
+ selection = 2;
+
+ /* emit the signal */
+ g_signal_emit (G_OBJECT (document), document_signals[SELECTION_CHANGED], 0, selection);
+}
+
+
+
+static void
+mousepad_document_notify_overwrite (GtkTextView *textview,
+ GParamSpec *pspec,
+ MousepadDocument *document)
+{
+ gboolean overwrite;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (document));
+ mousepad_return_if_fail (GTK_IS_TEXT_VIEW (textview));
+
+ /* whether overwrite is enabled */
+ overwrite = gtk_text_view_get_overwrite (textview);
+
+ /* emit the signal */
+ g_signal_emit (G_OBJECT (document), document_signals[OVERWRITE_CHANGED], 0, overwrite);
+}
+
+
+
+static void
+mousepad_document_notify_language (GtkSourceBuffer *buffer,
+ GParamSpec *pspec,
+ MousepadDocument *document)
+{
+ GtkSourceLanguage *language;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (document));
+ mousepad_return_if_fail (GTK_IS_SOURCE_BUFFER (buffer));
+
+ /* the new language */
+ language = gtk_source_buffer_get_language (buffer);
+
+ /* emit the signal */
+ g_signal_emit (G_OBJECT (document), document_signals[LANGUAGE_CHANGED], 0, language);
+}
+
+
+
+static void
+mousepad_document_drag_data_received (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ GtkSelectionData *selection_data,
+ guint info,
+ guint drag_time,
+ MousepadDocument *document)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (document));
+
+ /* emit the drag-data-received signal from the document when a tab or uri has been dropped */
+ if (info == TARGET_TEXT_URI_LIST || info == TARGET_GTK_NOTEBOOK_TAB)
+ g_signal_emit_by_name (G_OBJECT (document), "drag-data-received", context, x, y, selection_data, info, drag_time);
+}
+
+
+
+static void
+mousepad_document_filename_changed (MousepadDocument *document,
+ const gchar *filename)
+{
+ gchar *utf8_filename, *utf8_basename;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (document));
+ mousepad_return_if_fail (filename != NULL);
+
+ /* convert the title into a utf-8 valid version for display */
+ utf8_filename = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
+
+ if (G_LIKELY (utf8_filename))
+ {
+ /* create the display name */
+ utf8_basename = g_filename_display_basename (utf8_filename);
+
+ /* remove the old names */
+ g_free (document->priv->utf8_filename);
+ g_free (document->priv->utf8_basename);
+
+ /* set the new names */
+ document->priv->utf8_filename = utf8_filename;
+ document->priv->utf8_basename = utf8_basename;
+
+ /* update the tab label and tooltip */
+ if (G_UNLIKELY (document->priv->label))
+ {
+ /* set the tab label */
+ gtk_label_set_text (GTK_LABEL (document->priv->label), utf8_basename);
+
+ /* set the tab tooltip */
+ mousepad_widget_set_tooltip_text (document->priv->ebox, utf8_filename);
+
+ /* update label color */
+ mousepad_document_label_color (document);
+ }
+ }
+}
+
+
+
+static void
+mousepad_document_label_color (MousepadDocument *document)
+{
+ GdkColor green = {0, 0x0000, 0x9999, 0x0000};
+ GdkColor red = {0, 0xffff, 0x0000, 0x0000};
+ GdkColor *color;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (document));
+ mousepad_return_if_fail (GTK_IS_TEXT_BUFFER (document->buffer));
+ mousepad_return_if_fail (MOUSEPAD_IS_FILE (document->file));
+
+ if (document->priv->label)
+ {
+ /* label color */
+ if (gtk_text_buffer_get_modified (document->buffer))
+ color = &red;
+ else if (mousepad_file_get_read_only (document->file))
+ color = &green;
+ else
+ color = NULL;
+
+ /* update colors */
+ gtk_widget_modify_fg (document->priv->label, GTK_STATE_NORMAL, color);
+ gtk_widget_modify_fg (document->priv->label, GTK_STATE_ACTIVE, color);
+ }
+}
+
+
+
+void
+mousepad_document_set_overwrite (MousepadDocument *document,
+ gboolean overwrite)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (document));
+
+ gtk_text_view_set_overwrite (GTK_TEXT_VIEW (document->textview), overwrite);
+}
+
+
+
+void
+mousepad_document_set_word_wrap (MousepadDocument *document,
+ gboolean word_wrap)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (document));
+
+ /* store the setting */
+ document->priv->word_wrap = word_wrap;
+
+ /* set the wrapping mode */
+ gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (document->textview),
+ word_wrap ? GTK_WRAP_WORD : GTK_WRAP_NONE);
+}
+
+
+
+void
+mousepad_document_set_font (MousepadDocument *document,
+ const gchar *font_name)
+{
+ PangoFontDescription *font_desc;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (document));
+
+ if (G_LIKELY (font_name))
+ {
+ /* set the widget font */
+ font_desc = pango_font_description_from_string (font_name);
+ gtk_widget_modify_font (GTK_WIDGET (document->textview), font_desc);
+ pango_font_description_free (font_desc);
+ }
+}
+
+
+
+void
+mousepad_document_focus_textview (MousepadDocument *document)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (document));
+
+ /* focus the textview */
+ gtk_widget_grab_focus (GTK_WIDGET (document->textview));
+}
+
+
+
+void
+mousepad_document_send_signals (MousepadDocument *document)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (document));
+
+ /* re-send the cursor changed signal */
+ mousepad_document_notify_cursor_position (document->buffer, NULL, document);
+
+ /* re-send the overwrite signal */
+ mousepad_document_notify_overwrite (GTK_TEXT_VIEW (document->textview), NULL, document);
+
+ /* re-send the selection status */
+ mousepad_document_notify_has_selection (document->buffer, NULL, document);
+
+ /* re-send the language signal */
+ mousepad_document_notify_language (GTK_SOURCE_BUFFER (document->buffer), NULL, document);
+}
+
+
+
+GtkWidget *
+mousepad_document_get_tab_label (MousepadDocument *document)
+{
+ GtkWidget *hbox;
+ GtkWidget *button, *image;
+ GtkRcStyle *style;
+
+ /* create the box */
+ hbox = gtk_hbox_new (FALSE, 0);
+ gtk_widget_show (hbox);
+
+ /* the ebox */
+ document->priv->ebox = g_object_new (GTK_TYPE_EVENT_BOX, "border-width", 2, "visible-window", FALSE, NULL);
+ gtk_box_pack_start (GTK_BOX (hbox), document->priv->ebox, TRUE, TRUE, 0);
+ mousepad_widget_set_tooltip_text (document->priv->ebox, document->priv->utf8_filename);
+ gtk_widget_show (document->priv->ebox);
+
+ /* create the label */
+ document->priv->label = gtk_label_new (mousepad_document_get_basename (document));
+ gtk_container_add (GTK_CONTAINER (document->priv->ebox), document->priv->label);
+ gtk_widget_show (document->priv->label);
+
+ /* set label color */
+ mousepad_document_label_color (document);
+
+ /* create the button */
+ button = gtk_button_new ();
+ gtk_button_set_focus_on_click (GTK_BUTTON (button), FALSE);
+ gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
+ GTK_WIDGET_UNSET_FLAGS (button, GTK_CAN_DEFAULT | GTK_CAN_FOCUS);
+
+ /* make button a bit smaller */
+ style = gtk_rc_style_new ();
+ style->xthickness = style->ythickness = 0;
+ gtk_widget_modify_style (button, style);
+ g_object_unref (G_OBJECT (style));
+
+ /* pack button, add signal and tooltip */
+ mousepad_widget_set_tooltip_text (button, _("Close this tab"));
+ gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
+ g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (mousepad_document_tab_button_clicked), document);
+ gtk_widget_show (button);
+
+ /* button image */
+ image = gtk_image_new_from_stock (GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU);
+ gtk_container_add (GTK_CONTAINER (button), image);
+ gtk_widget_show (image);
+
+ return hbox;
+}
+
+
+
+static void
+mousepad_document_tab_button_clicked (GtkWidget *widget,
+ MousepadDocument *document)
+{
+ g_signal_emit (G_OBJECT (document), document_signals[CLOSE_TAB], 0);
+}
+
+
+
+const gchar *
+mousepad_document_get_basename (MousepadDocument *document)
+{
+ static gint untitled_counter = 0;
+
+ mousepad_return_val_if_fail (MOUSEPAD_IS_DOCUMENT (document), NULL);
+
+ /* check if there is a filename set */
+ if (document->priv->utf8_basename == NULL)
+ {
+ /* create an unique untitled document name */
+ document->priv->utf8_basename = g_strdup_printf ("%s %d", _("Untitled"), ++untitled_counter);
+ }
+
+ return document->priv->utf8_basename;
+}
+
+
+
+const gchar *
+mousepad_document_get_filename (MousepadDocument *document)
+{
+ mousepad_return_val_if_fail (MOUSEPAD_IS_DOCUMENT (document), NULL);
+
+ return document->priv->utf8_filename;
+}
+
+
+
+gboolean
+mousepad_document_get_word_wrap (MousepadDocument *document)
+{
+ mousepad_return_val_if_fail (MOUSEPAD_IS_DOCUMENT (document), FALSE);
+
+ return document->priv->word_wrap;
+}
diff --git a/mousepad/mousepad-document.h b/mousepad/mousepad-document.h
new file mode 100644
index 0000000..f2176c1
--- /dev/null
+++ b/mousepad/mousepad-document.h
@@ -0,0 +1,86 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __MOUSEPAD_DOCUMENT_H__
+#define __MOUSEPAD_DOCUMENT_H__
+
+G_BEGIN_DECLS
+
+#include <mousepad/mousepad-util.h>
+#include <mousepad/mousepad-file.h>
+#include <mousepad/mousepad-view.h>
+
+typedef struct _MousepadDocumentPrivate MousepadDocumentPrivate;
+typedef struct _MousepadDocumentClass MousepadDocumentClass;
+typedef struct _MousepadDocument MousepadDocument;
+
+#define MOUSEPAD_SCROLL_MARGIN 0.02
+
+#define MOUSEPAD_TYPE_DOCUMENT (mousepad_document_get_type ())
+#define MOUSEPAD_DOCUMENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MOUSEPAD_TYPE_DOCUMENT, MousepadDocument))
+#define MOUSEPAD_DOCUMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MOUSEPAD_TYPE_DOCUMENT, MousepadDocumentClass))
+#define MOUSEPAD_IS_DOCUMENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MOUSEPAD_TYPE_DOCUMENT))
+#define MOUSEPAD_IS_DOCUMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MOUSEPAD_TYPE_DOCUMENT))
+#define MOUSEPAD_DOCUMENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MOUSEPAD_TYPE_DOCUMENT, MousepadDocumentClass))
+
+struct _MousepadDocument
+{
+ GtkScrolledWindow __parent__;
+
+ /* private structure */
+ MousepadDocumentPrivate *priv;
+
+ /* file */
+ MousepadFile *file;
+
+ /* text buffer */
+ GtkTextBuffer *buffer;
+
+ /* text view */
+ MousepadView *textview;
+
+ /* the highlight tag */
+ GtkTextTag *tag;
+};
+
+GType mousepad_document_get_type (void) G_GNUC_CONST;
+
+MousepadDocument *mousepad_document_new (void);
+
+void mousepad_document_set_font (MousepadDocument *document,
+ const gchar *font_name);
+
+void mousepad_document_set_overwrite (MousepadDocument *document,
+ gboolean overwrite);
+
+void mousepad_document_set_word_wrap (MousepadDocument *document,
+ gboolean word_wrap);
+
+void mousepad_document_focus_textview (MousepadDocument *document);
+
+void mousepad_document_send_signals (MousepadDocument *document);
+
+GtkWidget *mousepad_document_get_tab_label (MousepadDocument *document);
+
+const gchar *mousepad_document_get_basename (MousepadDocument *document);
+
+const gchar *mousepad_document_get_filename (MousepadDocument *document);
+
+gboolean mousepad_document_get_word_wrap (MousepadDocument *document);
+
+G_END_DECLS
+
+#endif /* !__MOUSEPAD_DOCUMENT_H__ */
diff --git a/mousepad/mousepad-encoding-dialog.c b/mousepad/mousepad-encoding-dialog.c
new file mode 100644
index 0000000..7d4ce76
--- /dev/null
+++ b/mousepad/mousepad-encoding-dialog.c
@@ -0,0 +1,517 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <glib.h>
+#include <glib/gstdio.h>
+
+#include <mousepad/mousepad-private.h>
+#include <mousepad/mousepad-document.h>
+#include <mousepad/mousepad-encoding.h>
+#include <mousepad/mousepad-encoding-dialog.h>
+#include <mousepad/mousepad-preferences.h>
+#include <mousepad/mousepad-util.h>
+
+
+
+static void mousepad_encoding_dialog_finalize (GObject *object);
+static void mousepad_encoding_dialog_response (GtkDialog *dialog,
+ gint response_id);
+//~ static gboolean mousepad_encoding_dialog_test_encodings_idle (gpointer user_data);
+//~ static void mousepad_encoding_dialog_test_encodings_destroy (gpointer user_data);
+static void mousepad_encoding_dialog_test_encodings (MousepadEncodingDialog *dialog);
+static void mousepad_encoding_dialog_cancel_test_encodings (GtkWidget *button,
+ MousepadEncodingDialog *dialog);
+static void mousepad_encoding_dialog_read_file (MousepadEncodingDialog *dialog,
+ MousepadEncoding encoding);
+static void mousepad_encoding_dialog_button_toggled (GtkWidget *button,
+ MousepadEncodingDialog *dialog);
+static void mousepad_encoding_dialog_combo_changed (GtkComboBox *combo,
+ MousepadEncodingDialog *dialog);
+
+
+
+enum
+{
+ COLUMN_LABEL,
+ COLUMN_ID,
+ N_COLUMNS
+};
+
+struct _MousepadEncodingDialogClass
+{
+ GtkDialogClass __parent__;
+};
+
+struct _MousepadEncodingDialog
+{
+ GtkDialog __parent__;
+
+ /* the file */
+ MousepadDocument *document;
+
+ /* encoding test idle id */
+ guint timer_id;
+
+ /* boolean to cancel the testing loop */
+ guint cancel_testing : 1;
+
+ /* dialog widget */
+ GtkWidget *button_ok;
+ GtkWidget *button_cancel;
+ GtkWidget *error_box;
+ GtkWidget *error_label;
+ GtkWidget *progress_bar;
+
+ /* the three radio button */
+ GtkWidget *radio_utf8;
+ GtkWidget *radio_system;
+ GtkWidget *radio_other;
+
+ /* other encodings combo box */
+ GtkListStore *store;
+ GtkWidget *combo;
+};
+
+
+
+G_DEFINE_TYPE (MousepadEncodingDialog, mousepad_encoding_dialog, GTK_TYPE_DIALOG);
+
+
+
+static void
+mousepad_encoding_dialog_class_init (MousepadEncodingDialogClass *klass)
+{
+ GObjectClass *gobject_class;
+ GtkDialogClass *gtkdialog_class;
+
+ gobject_class = G_OBJECT_CLASS (klass);
+ gobject_class->finalize = mousepad_encoding_dialog_finalize;
+
+ gtkdialog_class = GTK_DIALOG_CLASS (klass);
+ gtkdialog_class->response = mousepad_encoding_dialog_response;
+}
+
+
+
+static void
+mousepad_encoding_dialog_init (MousepadEncodingDialog *dialog)
+{
+ const gchar *system_charset;
+ gchar *system_label;
+ GtkWidget *vbox, *hbox, *icon;
+ GtkCellRenderer *cell;
+
+ /* set some dialog properties */
+ gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
+ gtk_window_set_default_size (GTK_WINDOW (dialog), 550, 350);
+
+ /* add buttons */
+ gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
+ dialog->button_ok = gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_OK, GTK_RESPONSE_OK);
+
+ /* create the header */
+ mousepad_util_dialog_header (GTK_DIALOG (dialog), _("The document was not UTF-8 valid"),
+ _("Please select an encoding below."), GTK_STOCK_FILE);
+
+ /* dialog vbox */
+ vbox = gtk_vbox_new (FALSE, 6);
+ gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), vbox, TRUE, TRUE, 0);
+ gtk_widget_show (vbox);
+
+ hbox = gtk_hbox_new (FALSE, 6);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ /* encoding radio buttons */
+ dialog->radio_utf8 = gtk_radio_button_new_with_label (NULL, _("Default (UTF-8)"));
+ g_signal_connect (G_OBJECT (dialog->radio_utf8), "toggled", G_CALLBACK (mousepad_encoding_dialog_button_toggled), dialog);
+ gtk_box_pack_start (GTK_BOX (hbox), dialog->radio_utf8, FALSE, FALSE, 0);
+ gtk_widget_show (dialog->radio_utf8);
+
+ g_get_charset (&system_charset);
+ system_label = g_strdup_printf ("%s (%s)", _("System"), system_charset);
+ dialog->radio_system = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON (dialog->radio_utf8), system_label);
+ g_signal_connect (G_OBJECT (dialog->radio_system), "toggled", G_CALLBACK (mousepad_encoding_dialog_button_toggled), dialog);
+ gtk_box_pack_start (GTK_BOX (hbox), dialog->radio_system, FALSE, FALSE, 0);
+ gtk_widget_show (dialog->radio_system);
+ g_free (system_label);
+
+ dialog->radio_other = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON (dialog->radio_system), _("Other:"));
+ g_signal_connect (G_OBJECT (dialog->radio_other), "toggled", G_CALLBACK (mousepad_encoding_dialog_button_toggled), dialog);
+ gtk_box_pack_start (GTK_BOX (hbox), dialog->radio_other, FALSE, FALSE, 0);
+
+ /* create store */
+ dialog->store = gtk_list_store_new (N_COLUMNS, G_TYPE_STRING, G_TYPE_INT);
+
+ /* combobox with other charsets */
+ dialog->combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (dialog->store));
+ gtk_box_pack_start (GTK_BOX (hbox), dialog->combo, TRUE, TRUE, 0);
+ g_signal_connect (G_OBJECT (dialog->combo), "changed", G_CALLBACK (mousepad_encoding_dialog_combo_changed), dialog);
+
+ /* text renderer for 1st column */
+ cell = gtk_cell_renderer_text_new ();
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (dialog->combo), cell, TRUE);
+ gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (dialog->combo), cell, "text", COLUMN_LABEL, NULL);
+
+ /* progress bar */
+ dialog->progress_bar = gtk_progress_bar_new ();
+ gtk_box_pack_start (GTK_BOX (hbox), dialog->progress_bar, TRUE, TRUE, 0);
+ gtk_progress_bar_set_text (GTK_PROGRESS_BAR (dialog->progress_bar), _("Checking encodings..."));
+ gtk_widget_show (dialog->progress_bar);
+
+ /* cancel button */
+ dialog->button_cancel = gtk_button_new ();
+ gtk_box_pack_start (GTK_BOX (hbox), dialog->button_cancel, FALSE, FALSE, 0);
+ g_signal_connect (G_OBJECT (dialog->button_cancel), "clicked", G_CALLBACK (mousepad_encoding_dialog_cancel_test_encodings), dialog);
+ gtk_widget_show (dialog->button_cancel);
+
+ icon = gtk_image_new_from_stock (GTK_STOCK_CANCEL, GTK_ICON_SIZE_MENU);
+ gtk_container_add (GTK_CONTAINER (dialog->button_cancel), icon);
+ gtk_widget_show (icon);
+
+ /* error box */
+ dialog->error_box = gtk_hbox_new (FALSE, 6);
+ gtk_box_pack_start (GTK_BOX (vbox), dialog->error_box, FALSE, FALSE, 0);
+
+ /* error icon */
+ icon = gtk_image_new_from_stock (GTK_STOCK_DIALOG_ERROR, GTK_ICON_SIZE_BUTTON);
+ gtk_box_pack_start (GTK_BOX (dialog->error_box), icon, FALSE, FALSE, 0);
+ gtk_widget_show (icon);
+
+ /* error label */
+ dialog->error_label = gtk_label_new (NULL);
+ gtk_box_pack_start (GTK_BOX (dialog->error_box), dialog->error_label, FALSE, FALSE, 0);
+ gtk_label_set_use_markup (GTK_LABEL (dialog->error_label), TRUE);
+ gtk_widget_show (dialog->error_label);
+
+ /* create text view */
+ dialog->document = mousepad_document_new ();
+ gtk_box_pack_start (GTK_BOX (vbox), GTK_WIDGET (dialog->document), TRUE, TRUE, 0);
+ gtk_text_view_set_editable (GTK_TEXT_VIEW (dialog->document->textview), FALSE);
+ gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (dialog->document->textview), FALSE);
+ mousepad_view_set_line_numbers (dialog->document->textview, FALSE);
+ mousepad_document_set_word_wrap (dialog->document, FALSE);
+ gtk_widget_show (GTK_WIDGET (dialog->document));
+}
+
+
+
+static void
+mousepad_encoding_dialog_finalize (GObject *object)
+{
+ MousepadEncodingDialog *dialog = MOUSEPAD_ENCODING_DIALOG (object);
+
+ /* stop running timeout */
+ if (G_UNLIKELY (dialog->timer_id))
+ g_source_remove (dialog->timer_id);
+
+ /* clear and release store */
+ gtk_list_store_clear (dialog->store);
+ g_object_unref (G_OBJECT (dialog->store));
+
+ (*G_OBJECT_CLASS (mousepad_encoding_dialog_parent_class)->finalize) (object);
+}
+
+
+
+static void
+mousepad_encoding_dialog_response (GtkDialog *dialog,
+ gint response_id)
+{
+ /* make sure we cancel encoding testing asap */
+ MOUSEPAD_ENCODING_DIALOG (dialog)->cancel_testing = TRUE;
+}
+
+
+
+static gboolean
+mousepad_encoding_dialog_test_encodings_idle (gpointer user_data)
+{
+ MousepadEncodingDialog *dialog = MOUSEPAD_ENCODING_DIALOG (user_data);
+ const gchar *filename;
+ GMappedFile *mapped_file;
+ GError *error = NULL;
+ const gchar *contents;
+ gsize length, written;
+ guint i, n;
+ gchar *encoded;
+
+ GDK_THREADS_ENTER ();
+
+ /* get the filename */
+ filename = mousepad_file_get_filename (dialog->document->file);
+
+ /* check if the file exists */
+ if (filename && g_file_test (filename, G_FILE_TEST_EXISTS))
+ {
+ /* try to open the file */
+ mapped_file = g_mapped_file_new (filename, FALSE, &error);
+
+ if (G_LIKELY (mapped_file))
+ {
+ /* get the mapped file contents and length */
+ contents = g_mapped_file_get_contents (mapped_file);
+ length = g_mapped_file_get_length (mapped_file);
+
+ if (G_LIKELY (contents && length > 0))
+ {
+ /* test all the encodings */
+ for (i = 0, n = 0; i < n_encoding_infos && !dialog->cancel_testing; i++)
+ {
+ /* set progress bar fraction */
+ gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (dialog->progress_bar), (i + 1.00) / n_encoding_infos);
+
+ /* try to convert the content */
+ encoded = g_convert (contents, length, "UTF-8", encoding_infos[i].charset, NULL, &written, NULL);
+
+ if (G_LIKELY (encoded))
+ {
+ /* glib uses a faster validator when the string is nul-terminated */
+ if (G_LIKELY (written > 0 && encoded[written] == '\0'))
+ written = -1;
+
+ /* validate the encoded content */
+ if (G_LIKELY (g_utf8_validate (encoded, written, NULL)))
+ {
+ /* insert in the store */
+ gtk_list_store_insert_with_values (dialog->store, NULL, n++,
+ COLUMN_LABEL, encoding_infos[i].charset,
+ COLUMN_ID, encoding_infos[i].encoding, -1);
+ }
+
+ /* cleanup */
+ g_free (encoded);
+ }
+
+ /* iterate the main loop to update the gui */
+ while (gtk_events_pending ())
+ gtk_main_iteration ();
+ }
+ }
+
+ /* close the mapped file */
+#if GLIB_CHECK_VERSION (2, 21, 0)
+ g_mapped_file_unref (mapped_file);
+#else
+ g_mapped_file_free (mapped_file);
+#endif
+ }
+ }
+
+ /* hide progress bar and cancel button */
+ gtk_widget_hide (dialog->progress_bar);
+ gtk_widget_hide (dialog->button_cancel);
+
+ /* show the radio button and combo box */
+ gtk_widget_show (dialog->radio_other);
+ gtk_widget_show (dialog->combo);
+
+ /* select the first item */
+ gtk_combo_box_set_active (GTK_COMBO_BOX (dialog->combo), 0);
+
+ GDK_THREADS_LEAVE ();
+
+ return FALSE;
+}
+
+
+
+static void
+mousepad_encoding_dialog_test_encodings_destroy (gpointer user_data)
+{
+ MOUSEPAD_ENCODING_DIALOG (user_data)->timer_id = 0;
+}
+
+
+
+static void
+mousepad_encoding_dialog_test_encodings (MousepadEncodingDialog *dialog)
+{
+ if (G_LIKELY (dialog->timer_id == 0))
+ {
+ /* reset boolean */
+ dialog->cancel_testing = FALSE;
+
+ /* start a new idle function */
+ dialog->timer_id = g_idle_add_full (G_PRIORITY_LOW, mousepad_encoding_dialog_test_encodings_idle,
+ dialog, mousepad_encoding_dialog_test_encodings_destroy);
+ }
+}
+
+
+
+static void
+mousepad_encoding_dialog_cancel_test_encodings (GtkWidget *button,
+ MousepadEncodingDialog *dialog)
+{
+ /* cancel the testing loop */
+ dialog->cancel_testing = TRUE;
+}
+
+
+
+static void
+mousepad_encoding_dialog_read_file (MousepadEncodingDialog *dialog,
+ MousepadEncoding encoding)
+{
+ GtkTextIter start, end;
+ GError *error = NULL;
+ gchar *message;
+ gint result;
+
+ /* clear buffer */
+ gtk_text_buffer_get_bounds (dialog->document->buffer, &start, &end);
+ gtk_text_buffer_delete (dialog->document->buffer, &start, &end);
+
+ /* set encoding */
+ mousepad_file_set_encoding (dialog->document->file, encoding);
+
+ /* try to open the file */
+ result = mousepad_file_open (dialog->document->file, NULL, &error);
+
+ /* set sensitivity of the ok button */
+ gtk_widget_set_sensitive (dialog->button_ok, result == 0);
+
+ if (result == 0)
+ {
+ /* no error, hide the box */
+ gtk_widget_hide (dialog->error_box);
+ }
+ else
+ {
+ /* format message */
+ message = g_strdup_printf ("<b>%s.</b>", error->message);
+
+ /* set the error label */
+ gtk_label_set_markup (GTK_LABEL (dialog->error_label), message);
+
+ /* cleanup */
+ g_free (message);
+
+ /* show the error box */
+ gtk_widget_show (dialog->error_box);
+
+ /* clear the error */
+ g_error_free (error);
+ }
+}
+
+
+
+static void
+mousepad_encoding_dialog_button_toggled (GtkWidget *button,
+ MousepadEncodingDialog *dialog)
+{
+ /* ignore inactive buttons */
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
+ {
+ /* set sensitivity of the other combobox */
+ gtk_widget_set_sensitive (dialog->combo, (button == dialog->radio_other));
+
+ if (button == dialog->radio_utf8)
+ {
+ /* open the file */
+ mousepad_encoding_dialog_read_file (dialog, MOUSEPAD_ENCODING_UTF_8);
+ }
+ else if (button == dialog->radio_system)
+ {
+ /* open the file */
+ mousepad_encoding_dialog_read_file (dialog, mousepad_encoding_user ());
+ }
+ else
+ {
+ /* poke function */
+ mousepad_encoding_dialog_combo_changed (GTK_COMBO_BOX (dialog->combo), dialog);
+ }
+ }
+}
+
+
+
+static void
+mousepad_encoding_dialog_combo_changed (GtkComboBox *combo,
+ MousepadEncodingDialog *dialog)
+{
+ GtkTreeIter iter;
+ gint id;
+
+ /* get the selected item */
+ if (GTK_WIDGET_SENSITIVE (combo) && gtk_combo_box_get_active_iter (combo, &iter))
+ {
+ /* get the id */
+ gtk_tree_model_get (GTK_TREE_MODEL (dialog->store), &iter, COLUMN_ID, &id, -1);
+
+ /* open the file with other encoding */
+ mousepad_encoding_dialog_read_file (dialog, id);
+ }
+}
+
+
+
+GtkWidget *
+mousepad_encoding_dialog_new (GtkWindow *parent,
+ MousepadFile *file)
+{
+ MousepadEncodingDialog *dialog;
+
+ mousepad_return_val_if_fail (GTK_IS_WINDOW (parent), NULL);
+ mousepad_return_val_if_fail (MOUSEPAD_IS_FILE (file), NULL);
+
+ /* create the dialog */
+ dialog = g_object_new (MOUSEPAD_TYPE_ENCODING_DIALOG, NULL);
+
+ /* set parent window */
+ gtk_window_set_transient_for (GTK_WINDOW (dialog), parent);
+
+ /* set the filename */
+ mousepad_file_set_filename (dialog->document->file, mousepad_file_get_filename (file));
+
+ /* start with the system encoding */
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dialog->radio_system), TRUE);
+
+ /* queue idle function */
+ mousepad_encoding_dialog_test_encodings (dialog);
+
+ return GTK_WIDGET (dialog);
+}
+
+
+
+MousepadEncoding
+mousepad_encoding_dialog_get_encoding (MousepadEncodingDialog *dialog)
+{
+ mousepad_return_val_if_fail (MOUSEPAD_IS_ENCODING_DIALOG (dialog), MOUSEPAD_ENCODING_NONE);
+
+ return mousepad_file_get_encoding (dialog->document->file);
+}
+
+
+
+const gchar *
+mousepad_encoding_dialog_get_encoding_custom (MousepadEncodingDialog *dialog)
+{
+ mousepad_return_val_if_fail (MOUSEPAD_IS_ENCODING_DIALOG (dialog), NULL);
+
+ return NULL;
+}
diff --git a/mousepad/mousepad-encoding-dialog.h b/mousepad/mousepad-encoding-dialog.h
new file mode 100644
index 0000000..ca4ee7f
--- /dev/null
+++ b/mousepad/mousepad-encoding-dialog.h
@@ -0,0 +1,45 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __MOUSEPAD_ENCODING_DIALOG_H__
+#define __MOUSEPAD_ENCODING_DIALOG_H__
+
+G_BEGIN_DECLS
+
+#include <mousepad/mousepad-encoding.h>
+
+#define MOUSEPAD_TYPE_ENCODING_DIALOG (mousepad_encoding_dialog_get_type ())
+#define MOUSEPAD_ENCODING_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MOUSEPAD_TYPE_ENCODING_DIALOG, MousepadEncodingDialog))
+#define MOUSEPAD_ENCODING_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MOUSEPAD_TYPE_ENCODING_DIALOG, MousepadEncodingDialogClass))
+#define MOUSEPAD_IS_ENCODING_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MOUSEPAD_TYPE_ENCODING_DIALOG))
+#define MOUSEPAD_IS_ENCODING_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MOUSEPAD_TYPE_ENCODING_DIALOG))
+#define MOUSEPAD_ENCODING_DIALOG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MOUSEPAD_TYPE_ENCODING_DIALOG, MousepadEncodingDialogClass))
+
+typedef struct _MousepadEncodingDialogClass MousepadEncodingDialogClass;
+typedef struct _MousepadEncodingDialog MousepadEncodingDialog;
+
+GType mousepad_encoding_dialog_get_type (void) G_GNUC_CONST;
+
+GtkWidget *mousepad_encoding_dialog_new (GtkWindow *parent,
+ MousepadFile *file);
+
+MousepadEncoding mousepad_encoding_dialog_get_encoding (MousepadEncodingDialog *dialog);
+
+const gchar *mousepad_encoding_dialog_get_encoding_custom (MousepadEncodingDialog *dialog);
+
+G_END_DECLS
+
+#endif /* !__MOUSEPAD_ENCODING_DIALOG_H__ */
diff --git a/mousepad/mousepad-encoding.c b/mousepad/mousepad-encoding.c
new file mode 100644
index 0000000..ac2f19a
--- /dev/null
+++ b/mousepad/mousepad-encoding.c
@@ -0,0 +1,201 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <glib.h>
+
+#include <mousepad/mousepad-private.h>
+#include <mousepad/mousepad-encoding.h>
+
+
+
+const MousepadEncodingInfo encoding_infos[] =
+{
+ /* west european */
+ { MOUSEPAD_ENCODING_ISO_8859_14, "ISO-8859-14", N_("Celtic") },
+ { MOUSEPAD_ENCODING_ISO_8859_7, "ISO-8859-7", N_("Greek") },
+ { MOUSEPAD_ENCODING_WINDOWS_1253, "WINDOWS-1253", N_("Greek") },
+ { MOUSEPAD_ENCODING_ISO_8859_10, "ISO-8859-10", N_("Nordic") },
+ { MOUSEPAD_ENCODING_ISO_8859_3, "ISO-8859-3", N_("South European") },
+ { MOUSEPAD_ENCODING_IBM_850, "IBM850", N_("Western") },
+ { MOUSEPAD_ENCODING_ISO_8859_1, "ISO-8859-1", N_("Western") },
+ { MOUSEPAD_ENCODING_ISO_8859_15, "ISO-8859-15", N_("Western") },
+ { MOUSEPAD_ENCODING_WINDOWS_1252, "WINDOWS-1252", N_("Western") },
+
+ /* east european */
+ { MOUSEPAD_ENCODING_ISO_8859_4, "ISO-8859-4", N_("Baltic") },
+ { MOUSEPAD_ENCODING_ISO_8859_13, "ISO-8859-13", N_("Baltic") },
+ { MOUSEPAD_ENCODING_WINDOWS_1257, "WINDOWS-1257", N_("Baltic") },
+ { MOUSEPAD_ENCODING_IBM_852, "IBM852", N_("Central European") },
+ { MOUSEPAD_ENCODING_ISO_8859_2, "ISO-8859-2", N_("Central European") },
+ { MOUSEPAD_ENCODING_WINDOWS_1250, "WINDOWS-1250", N_("Central European") },
+ { MOUSEPAD_ENCODING_IBM_855, "IBM855", N_("Cyrillic") },
+ { MOUSEPAD_ENCODING_ISO_8859_5, "ISO-8859-5", N_("Cyrillic") },
+ { MOUSEPAD_ENCODING_ISO_IR_111, "ISO-IR-111", N_("Cyrillic") },
+ { MOUSEPAD_ENCODING_KOI8_R, "KOI8R", N_("Cyrillic") },
+ { MOUSEPAD_ENCODING_WINDOWS_1251, "WINDOWS-1251", N_("Cyrillic") },
+ { MOUSEPAD_ENCODING_CP_866, "CP866", N_("Cyrillic/Russian") },
+ { MOUSEPAD_ENCODING_KOI8_U, "KOI8U", N_("Cyrillic/Ukrainian") },
+ { MOUSEPAD_ENCODING_ISO_8859_16, "ISO-8859-16", N_("Romanian") },
+
+ /* middle eastern */
+ { MOUSEPAD_ENCODING_IBM_864, "IBM864", N_("Arabic") },
+ { MOUSEPAD_ENCODING_ISO_8859_6, "ISO-8859-6", N_("Arabic") },
+ { MOUSEPAD_ENCODING_WINDOWS_1256, "WINDOWS-1256", N_("Arabic") },
+ { MOUSEPAD_ENCODING_IBM_862, "IBM862", N_("Hebrew") },
+ { MOUSEPAD_ENCODING_ISO_8859_8_I, "ISO-8859-8-I", N_("Hebrew") },
+ { MOUSEPAD_ENCODING_WINDOWS_1255, "WINDOWS-1255", N_("Hebrew") },
+ { MOUSEPAD_ENCODING_ISO_8859_8, "ISO-8859-8", N_("Hebrew Visual") },
+
+ /* asian */
+ { MOUSEPAD_ENCODING_ARMSCII_8, "ARMSCII-8", N_("Armenian") },
+ { MOUSEPAD_ENCODING_GEOSTD8, "GEORGIAN-ACADEMY", N_("Georgian") },
+ { MOUSEPAD_ENCODING_TIS_620, "TIS-620", N_("Thai") },
+ { MOUSEPAD_ENCODING_IBM_857, "IBM857", N_("Turkish") },
+ { MOUSEPAD_ENCODING_WINDOWS_1254, "WINDOWS-1254", N_("Turkish") },
+ { MOUSEPAD_ENCODING_ISO_8859_9, "ISO-8859-9", N_("Turkish") },
+ { MOUSEPAD_ENCODING_TCVN, "TCVN", N_("Vietnamese") },
+ { MOUSEPAD_ENCODING_VISCII, "VISCII", N_("Vietnamese") },
+ { MOUSEPAD_ENCODING_WINDOWS_1258, "WINDOWS-1258", N_("Vietnamese") },
+
+ /* unicode */
+ { MOUSEPAD_ENCODING_UTF_7, "UTF-7", N_("Unicode") },
+ { MOUSEPAD_ENCODING_UTF_8, "UTF-8", N_("Unicode") },
+ { MOUSEPAD_ENCODING_UTF_16LE, "UTF-16LE", N_("Unicode") },
+ { MOUSEPAD_ENCODING_UTF_16BE, "UTF-16BE", N_("Unicode") },
+ { MOUSEPAD_ENCODING_UCS_2LE, "UCS-2LE", N_("Unicode") },
+ { MOUSEPAD_ENCODING_UCS_2BE, "UCS-2BE", N_("Unicode") },
+ { MOUSEPAD_ENCODING_UTF_32LE, "UTF-32LE", N_("Unicode") },
+ { MOUSEPAD_ENCODING_UTF_32BE, "UTF-32BE", N_("Unicode") },
+
+ /* east asian */
+ { MOUSEPAD_ENCODING_GB18030, "GB18030", N_("Chinese Simplified") },
+ { MOUSEPAD_ENCODING_GB2312, "GB2312", N_("Chinese Simplified") },
+ { MOUSEPAD_ENCODING_GBK, "GBK", N_("Chinese Simplified") },
+ { MOUSEPAD_ENCODING_HZ, "HZ", N_("Chinese Simplified") },
+ { MOUSEPAD_ENCODING_BIG5, "BIG5", N_("Chinese Traditional") },
+ { MOUSEPAD_ENCODING_BIG5_HKSCS, "BIG5-HKSCS", N_("Chinese Traditional") },
+ { MOUSEPAD_ENCODING_EUC_TW, "EUC-TW", N_("Chinese Traditional") },
+ { MOUSEPAD_ENCODING_EUC_JP, "EUC-JP", N_("Japanese") },
+ { MOUSEPAD_ENCODING_ISO_2022_JP, "ISO-2022-JP", N_("Japanese") },
+ { MOUSEPAD_ENCODING_SHIFT_JIS, "SHIFT_JIS", N_("Japanese") },
+ { MOUSEPAD_ENCODING_EUC_KR, "EUC-KR", N_("Korean") },
+ { MOUSEPAD_ENCODING_ISO_2022_KR, "ISO-2022-KR", N_("Korean") },
+ { MOUSEPAD_ENCODING_JOHAB, "JOHAB", N_("Korean") },
+ { MOUSEPAD_ENCODING_UHC, "UHC", N_("Korean") }
+};
+
+
+
+guint n_encoding_infos = G_N_ELEMENTS (encoding_infos);
+
+
+
+const gchar *
+mousepad_encoding_get_charset (MousepadEncoding encoding)
+{
+ guint i;
+
+ /* try to find and return the charset */
+ for (i = 0; i < G_N_ELEMENTS (encoding_infos); i++)
+ if (encoding_infos[i].encoding == encoding)
+ return encoding_infos[i].charset;
+
+ return NULL;
+}
+
+
+
+const gchar *
+mousepad_encoding_get_name (MousepadEncoding encoding)
+{
+ guint i;
+
+ /* try to find and return the translated name */
+ for (i = 0; i < G_N_ELEMENTS (encoding_infos); i++)
+ if (encoding_infos[i].encoding == encoding)
+ return _(encoding_infos[i].name);
+
+ return NULL;
+}
+
+
+
+MousepadEncoding
+mousepad_encoding_user (void)
+{
+ static MousepadEncoding encoding = MOUSEPAD_ENCODING_NONE;
+ const gchar *charset;
+
+ if (G_UNLIKELY (encoding == MOUSEPAD_ENCODING_NONE))
+ {
+ /* try to find the user charset */
+ if (g_get_charset (&charset) == FALSE)
+ encoding = mousepad_encoding_find (charset);
+
+ /* default to utf-8 */
+ if (G_LIKELY (encoding == MOUSEPAD_ENCODING_NONE))
+ encoding = MOUSEPAD_ENCODING_UTF_8;
+ }
+
+ return encoding;
+}
+
+
+
+MousepadEncoding
+mousepad_encoding_find (const gchar *charset)
+{
+ guint i;
+
+ /* check for invalid strings */
+ if (G_UNLIKELY (charset == NULL || *charset == '\0'))
+ return MOUSEPAD_ENCODING_NONE;
+
+ /* find the charset */
+ for (i = 0; i < G_N_ELEMENTS (encoding_infos); i++)
+ if (strcasecmp (encoding_infos[i].charset, charset) == 0)
+ return encoding_infos[i].encoding;
+
+ /* no encoding charset found */
+ return MOUSEPAD_ENCODING_NONE;
+}
+
+
+
+gboolean
+mousepad_encoding_is_unicode (MousepadEncoding encoding)
+{
+ const gchar *charset;
+
+ /* get the characterset name */
+ charset = mousepad_encoding_get_charset (encoding);
+
+ /* check for unicode charset */
+ if (charset != NULL && (strncmp (charset, "UTF", 3) == 0
+ || strncmp (charset, "UCS", 3) == 0))
+ return TRUE;
+
+ /* not an unicode charset */
+ return FALSE;
+}
diff --git a/mousepad/mousepad-encoding.h b/mousepad/mousepad-encoding.h
new file mode 100644
index 0000000..eee8528
--- /dev/null
+++ b/mousepad/mousepad-encoding.h
@@ -0,0 +1,125 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __MOUSEPAD_ENCODINGS_H__
+#define __MOUSEPAD_ENCODINGS_H__
+
+G_BEGIN_DECLS
+
+typedef enum _MousepadEncoding MousepadEncoding;
+typedef struct _MousepadEncodingInfo MousepadEncodingInfo;
+
+enum _MousepadEncoding
+{
+ MOUSEPAD_ENCODING_NONE,
+ MOUSEPAD_ENCODING_CUSTOM,
+
+ MOUSEPAD_ENCODING_ISO_8859_1,
+ MOUSEPAD_ENCODING_ISO_8859_2,
+ MOUSEPAD_ENCODING_ISO_8859_3,
+ MOUSEPAD_ENCODING_ISO_8859_4,
+ MOUSEPAD_ENCODING_ISO_8859_5,
+ MOUSEPAD_ENCODING_ISO_8859_6,
+ MOUSEPAD_ENCODING_ISO_8859_7,
+ MOUSEPAD_ENCODING_ISO_8859_8,
+ MOUSEPAD_ENCODING_ISO_8859_8_I,
+ MOUSEPAD_ENCODING_ISO_8859_9,
+ MOUSEPAD_ENCODING_ISO_8859_10,
+ MOUSEPAD_ENCODING_ISO_8859_13,
+ MOUSEPAD_ENCODING_ISO_8859_14,
+ MOUSEPAD_ENCODING_ISO_8859_15,
+ MOUSEPAD_ENCODING_ISO_8859_16,
+
+ MOUSEPAD_ENCODING_UTF_7,
+ MOUSEPAD_ENCODING_UTF_8,
+ MOUSEPAD_ENCODING_UTF_16LE,
+ MOUSEPAD_ENCODING_UTF_16BE,
+ MOUSEPAD_ENCODING_UCS_2LE,
+ MOUSEPAD_ENCODING_UCS_2BE,
+ MOUSEPAD_ENCODING_UTF_32LE,
+ MOUSEPAD_ENCODING_UTF_32BE,
+
+ MOUSEPAD_ENCODING_ARMSCII_8,
+ MOUSEPAD_ENCODING_BIG5,
+ MOUSEPAD_ENCODING_BIG5_HKSCS,
+ MOUSEPAD_ENCODING_CP_866,
+
+ MOUSEPAD_ENCODING_EUC_JP,
+ MOUSEPAD_ENCODING_EUC_KR,
+ MOUSEPAD_ENCODING_EUC_TW,
+
+ MOUSEPAD_ENCODING_GB18030,
+ MOUSEPAD_ENCODING_GB2312,
+ MOUSEPAD_ENCODING_GBK,
+ MOUSEPAD_ENCODING_GEOSTD8,
+ MOUSEPAD_ENCODING_HZ,
+
+ MOUSEPAD_ENCODING_IBM_850,
+ MOUSEPAD_ENCODING_IBM_852,
+ MOUSEPAD_ENCODING_IBM_855,
+ MOUSEPAD_ENCODING_IBM_857,
+ MOUSEPAD_ENCODING_IBM_862,
+ MOUSEPAD_ENCODING_IBM_864,
+
+ MOUSEPAD_ENCODING_ISO_2022_JP,
+ MOUSEPAD_ENCODING_ISO_2022_KR,
+ MOUSEPAD_ENCODING_ISO_IR_111,
+ MOUSEPAD_ENCODING_JOHAB,
+ MOUSEPAD_ENCODING_KOI8_R,
+ MOUSEPAD_ENCODING_KOI8_U,
+
+ MOUSEPAD_ENCODING_SHIFT_JIS,
+ MOUSEPAD_ENCODING_TCVN,
+ MOUSEPAD_ENCODING_TIS_620,
+ MOUSEPAD_ENCODING_UHC,
+ MOUSEPAD_ENCODING_VISCII,
+
+ MOUSEPAD_ENCODING_WINDOWS_1250,
+ MOUSEPAD_ENCODING_WINDOWS_1251,
+ MOUSEPAD_ENCODING_WINDOWS_1252,
+ MOUSEPAD_ENCODING_WINDOWS_1253,
+ MOUSEPAD_ENCODING_WINDOWS_1254,
+ MOUSEPAD_ENCODING_WINDOWS_1255,
+ MOUSEPAD_ENCODING_WINDOWS_1256,
+ MOUSEPAD_ENCODING_WINDOWS_1257,
+ MOUSEPAD_ENCODING_WINDOWS_1258
+};
+
+struct _MousepadEncodingInfo
+{
+ MousepadEncoding encoding;
+ const gchar *charset;
+ const gchar *name;
+};
+
+
+extern const MousepadEncodingInfo encoding_infos[];
+
+extern guint n_encoding_infos;
+
+const gchar *mousepad_encoding_get_charset (MousepadEncoding encoding);
+
+const gchar *mousepad_encoding_get_name (MousepadEncoding encoding);
+
+MousepadEncoding mousepad_encoding_user (void);
+
+MousepadEncoding mousepad_encoding_find (const gchar *charset);
+
+gboolean mousepad_encoding_is_unicode (MousepadEncoding encoding);
+
+G_END_DECLS
+
+#endif /* !__MOUSEPAD_ENCODINGS_H__ */
diff --git a/mousepad/mousepad-file.c b/mousepad/mousepad-file.c
new file mode 100644
index 0000000..0148b69
--- /dev/null
+++ b/mousepad/mousepad-file.c
@@ -0,0 +1,919 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include <glib.h>
+#include <glib/gstdio.h>
+
+#include <mousepad/mousepad-private.h>
+#include <mousepad/mousepad-file.h>
+
+
+
+enum
+{
+ /* EXTERNALLY_MODIFIED, */
+ FILENAME_CHANGED,
+ READONLY_CHANGED,
+ LAST_SIGNAL
+};
+
+struct _MousepadFileClass
+{
+ GObjectClass __parent__;
+};
+
+struct _MousepadFile
+{
+ GObject __parent__;
+
+ /* the text buffer this file belongs to */
+ GtkTextBuffer *buffer;
+
+ /* filename */
+ gchar *filename;
+
+ /* encoding of the file */
+ MousepadEncoding encoding;
+
+ /* line ending of the file */
+ MousepadLineEnding line_ending;
+
+ /* our last modification time */
+ gint mtime;
+
+ /* if file is read-only */
+ guint readonly : 1;
+
+ /* whether we write the bom at the start of the file */
+ guint write_bom : 1;
+};
+
+
+
+static void mousepad_file_finalize (GObject *object);
+static void mousepad_file_set_readonly (MousepadFile *file,
+ gboolean readonly);
+
+
+
+static guint file_signals[LAST_SIGNAL];
+
+
+
+G_DEFINE_TYPE (MousepadFile, mousepad_file, G_TYPE_OBJECT);
+
+
+
+static void
+mousepad_file_class_init (MousepadFileClass *klass)
+{
+ GObjectClass *gobject_class;
+
+ gobject_class = G_OBJECT_CLASS (klass);
+ gobject_class->finalize = mousepad_file_finalize;
+
+#if 0
+ /* TODO implement this signal */
+ file_signals[EXTERNALLY_MODIFIED] =
+ g_signal_new (I_("externally-modified"),
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL,
+ g_cclosure_marshal_VOID__BOOLEAN,
+ G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
+#endif
+
+ file_signals[READONLY_CHANGED] =
+ g_signal_new (I_("readonly-changed"),
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL,
+ g_cclosure_marshal_VOID__BOOLEAN,
+ G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
+
+ file_signals[FILENAME_CHANGED] =
+ g_signal_new (I_("filename-changed"),
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE, 1, G_TYPE_STRING);
+}
+
+
+
+static void
+mousepad_file_init (MousepadFile *file)
+{
+ /* initialize */
+ file->filename = NULL;
+ file->encoding = MOUSEPAD_ENCODING_UTF_8;
+#ifdef G_OS_WIN32
+ file->line_ending = MOUSEPAD_EOL_DOS;
+#else
+ file->line_ending = MOUSEPAD_EOL_UNIX;
+#endif
+ file->readonly = TRUE;
+ file->mtime = 0;
+ file->write_bom = FALSE;
+}
+
+
+
+static void
+mousepad_file_finalize (GObject *object)
+{
+ MousepadFile *file = MOUSEPAD_FILE (object);
+
+ /* cleanup */
+ g_free (file->filename);
+
+ /* release the reference from the buffer */
+ g_object_unref (G_OBJECT (file->buffer));
+
+ (*G_OBJECT_CLASS (mousepad_file_parent_class)->finalize) (object);
+}
+
+
+
+static void
+mousepad_file_set_readonly (MousepadFile *file,
+ gboolean readonly)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_FILE (file));
+
+ if (G_LIKELY (file->readonly != readonly))
+ {
+ /* store new value */
+ file->readonly = readonly;
+
+ /* emit signal */
+ g_signal_emit (G_OBJECT (file), file_signals[READONLY_CHANGED], 0, readonly);
+ }
+}
+
+
+
+static MousepadEncoding
+mousepad_file_encoding_read_bom (const gchar *contents,
+ gsize length,
+ gsize *bom_length)
+{
+ const guchar *bom = (const guchar *) contents;
+ MousepadEncoding encoding = MOUSEPAD_ENCODING_NONE;
+ gsize bytes = 0;
+
+ mousepad_return_val_if_fail (contents != NULL && length > 0, MOUSEPAD_ENCODING_NONE);
+ mousepad_return_val_if_fail (contents != NULL && length > 0, MOUSEPAD_ENCODING_NONE);
+
+ switch (bom[0])
+ {
+ case 0xef:
+ if (length >= 3 && bom[1] == 0xbb && bom[2] == 0xbf)
+ {
+ bytes = 3;
+ encoding = MOUSEPAD_ENCODING_UTF_8;
+ }
+ break;
+
+ case 0x00:
+ if (length >= 4 && bom[1] == 0x00 && bom[2] == 0xfe && bom[3] == 0xff)
+ {
+ bytes = 4;
+ encoding = MOUSEPAD_ENCODING_UTF_32BE;
+ }
+ break;
+
+ case 0xff:
+ if (length >= 4 && bom[1] == 0xfe && bom[2] == 0x00 && bom[3] == 0x00)
+ {
+ bytes = 4;
+ encoding = MOUSEPAD_ENCODING_UTF_32LE;
+ }
+ else if (length >= 2 && bom[1] == 0xfe)
+ {
+ bytes = 2;
+ encoding = MOUSEPAD_ENCODING_UTF_16LE;
+ }
+ break;
+
+ case 0x2b:
+ if (length >= 4 && (bom[0] == 0x2b && bom[1] == 0x2f && bom[2] == 0x76) &&
+ (bom[3] == 0x38 || bom[3] == 0x39 || bom[3] == 0x2b || bom[3] == 0x2f))
+ {
+ bytes = 4;
+ encoding = MOUSEPAD_ENCODING_UTF_7;
+ }
+ break;
+
+ case 0xfe:
+ if (length >= 2 && bom[1] == 0xff)
+ {
+ bytes = 2;
+ encoding = MOUSEPAD_ENCODING_UTF_16BE;
+ }
+ break;
+ }
+
+ if (bom_length)
+ *bom_length = bytes;
+
+ return encoding;
+}
+
+
+
+MousepadFile *
+mousepad_file_new (GtkTextBuffer *buffer)
+{
+ MousepadFile *file;
+
+ mousepad_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
+
+ file = g_object_new (MOUSEPAD_TYPE_FILE, NULL);
+
+ /* set the buffer */
+ file->buffer = g_object_ref (G_OBJECT (buffer));
+
+ return file;
+}
+
+
+
+void
+mousepad_file_set_filename (MousepadFile *file,
+ const gchar *filename)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_FILE (file));
+
+ /* free the old filename */
+ g_free (file->filename);
+
+ /* set the filename */
+ file->filename = g_strdup (filename);
+
+ /* send a signal that the name has been changed */
+ g_signal_emit (G_OBJECT (file), file_signals[FILENAME_CHANGED], 0, file->filename);
+}
+
+
+
+const gchar *
+mousepad_file_get_filename (MousepadFile *file)
+{
+ mousepad_return_val_if_fail (MOUSEPAD_IS_FILE (file), NULL);
+
+ return file->filename;
+}
+
+
+
+gchar *
+mousepad_file_get_uri (MousepadFile *file)
+{
+ mousepad_return_val_if_fail (MOUSEPAD_IS_FILE (file), NULL);
+
+ return g_filename_to_uri (file->filename, NULL, NULL);
+}
+
+
+
+void
+mousepad_file_set_encoding (MousepadFile *file,
+ MousepadEncoding encoding)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_FILE (file));
+
+ /* set new encoding */
+ file->encoding = encoding;
+}
+
+
+
+MousepadEncoding
+mousepad_file_get_encoding (MousepadFile *file)
+{
+ mousepad_return_val_if_fail (MOUSEPAD_IS_FILE (file), MOUSEPAD_ENCODING_NONE);
+
+ return file->encoding;
+}
+
+
+
+gboolean
+mousepad_file_get_read_only (MousepadFile *file)
+{
+ mousepad_return_val_if_fail (MOUSEPAD_IS_FILE (file), FALSE);
+
+ return file->filename ? file->readonly : FALSE;
+}
+
+
+
+void
+mousepad_file_set_write_bom (MousepadFile *file,
+ gboolean write_bom)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_FILE (file));
+ mousepad_return_if_fail (mousepad_encoding_is_unicode (file->encoding));
+
+ /* set new value */
+ file->write_bom = write_bom;
+}
+
+
+
+gboolean
+mousepad_file_get_write_bom (MousepadFile *file,
+ gboolean *sensitive)
+{
+ mousepad_return_val_if_fail (MOUSEPAD_IS_FILE (file), FALSE);
+
+ /* return if we can write a bom */
+ if (G_LIKELY (sensitive))
+ *sensitive = mousepad_encoding_is_unicode (file->encoding);
+
+ return file->write_bom;
+}
+
+
+
+void
+mousepad_file_set_line_ending (MousepadFile *file,
+ MousepadLineEnding line_ending)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_FILE (file));
+
+ file->line_ending = line_ending;
+}
+
+
+
+MousepadLineEnding
+mousepad_file_get_line_ending (MousepadFile *file)
+{
+ return file->line_ending;
+}
+
+
+
+void
+mousepad_file_set_language (MousepadFile *file,
+ GtkSourceLanguage *language)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_FILE (file));
+ mousepad_return_if_fail (GTK_IS_SOURCE_BUFFER (file->buffer));
+
+ gtk_source_buffer_set_language (GTK_SOURCE_BUFFER (file->buffer), language);
+}
+
+
+
+GtkSourceLanguage *
+mousepad_file_get_language (MousepadFile *file)
+{
+ mousepad_return_val_if_fail (MOUSEPAD_IS_FILE (file), NULL);
+ mousepad_return_val_if_fail (GTK_IS_SOURCE_BUFFER (file->buffer), NULL);
+
+ return gtk_source_buffer_get_language (GTK_SOURCE_BUFFER (file->buffer));
+}
+
+
+
+void
+mousepad_file_set_language_id (MousepadFile *file,
+ const gchar *language_id)
+{
+ GtkSourceLanguage *lang;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_FILE (file));
+ mousepad_return_if_fail (GTK_IS_SOURCE_BUFFER (file->buffer));
+
+ if (G_UNLIKELY (language_id == NULL))
+ {
+ gtk_source_buffer_set_language (GTK_SOURCE_BUFFER (file->buffer), NULL);
+ return;
+ }
+
+ lang = gtk_source_language_manager_get_language (gtk_source_language_manager_get_default (), language_id);
+ mousepad_file_set_language (file, lang);
+}
+
+
+
+const gchar *
+mousepad_file_get_language_id (MousepadFile *file)
+{
+ GtkSourceLanguage *lang;
+
+ lang = mousepad_file_get_language (file);
+ return (lang != NULL) ? gtk_source_language_get_id (lang) : NULL;
+}
+
+
+
+GtkSourceLanguage *
+mousepad_file_guess_language (MousepadFile *file)
+{
+ gchar *content_type;
+ gboolean result_uncertain;
+ GtkSourceLanguage *language = NULL;
+
+ content_type = g_content_type_guess (file->filename, NULL, 0, &result_uncertain);
+ if (result_uncertain)
+ {
+ g_free (content_type);
+ content_type = NULL;
+ }
+
+ language = gtk_source_language_manager_guess_language (gtk_source_language_manager_get_default (),
+ file->filename,
+ content_type);
+ g_free (content_type);
+
+ return language;
+}
+
+
+
+gint
+mousepad_file_open (MousepadFile *file,
+ const gchar *template_filename,
+ GError **error)
+{
+ GMappedFile *mapped_file;
+ const gchar *filename;
+ gint retval = ERROR_READING_FAILED;
+ gsize length, written;
+ gsize bom_length;
+ const gchar *contents;
+ gchar *encoded = NULL;
+ const gchar *charset;
+ GtkTextIter start_iter, end_iter;
+ struct stat statb;
+ const gchar *end, *n, *m;
+ MousepadEncoding bom_encoding;
+
+ mousepad_return_val_if_fail (MOUSEPAD_IS_FILE (file), FALSE);
+ mousepad_return_val_if_fail (GTK_IS_TEXT_BUFFER (file->buffer), FALSE);
+ mousepad_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+ mousepad_return_val_if_fail (file->filename != NULL || template_filename != NULL, FALSE);
+
+ /* get the filename */
+ if (G_UNLIKELY (template_filename != NULL))
+ filename = template_filename;
+ else
+ filename = file->filename;
+
+ /* check if the file exists, if not, it's a filename from the command line */
+ if (g_file_test (filename, G_FILE_TEST_EXISTS) == FALSE)
+ {
+ /* update readonly status */
+ mousepad_file_set_readonly (file, FALSE);
+
+ return 0;
+ }
+
+ /* try to open the file */
+ mapped_file = g_mapped_file_new (filename, FALSE, error);
+
+ if (G_LIKELY (mapped_file))
+ {
+ /* get the mapped file contents and length */
+ contents = g_mapped_file_get_contents (mapped_file);
+ length = g_mapped_file_get_length (mapped_file);
+
+ if (G_LIKELY (contents != NULL && length > 0))
+ {
+ /* detect if there is a bom with the encoding type */
+ bom_encoding = mousepad_file_encoding_read_bom (contents, length, &bom_length);
+ if (G_UNLIKELY (bom_encoding != MOUSEPAD_ENCODING_NONE))
+ {
+ /* we've found a valid bom at the start of the contents */
+ file->write_bom = TRUE;
+
+ /* advance the contents offset and decrease length */
+ contents += bom_length;
+ length -= bom_length;
+
+ /* set the detected encoding */
+ file->encoding = bom_encoding;
+ }
+
+ /* handle encoding and check for utf-8 valid text */
+ if (G_LIKELY (file->encoding == MOUSEPAD_ENCODING_UTF_8))
+ {
+ validate:
+
+ /* glib uses a faster validator when the string is nul-terminated */
+ if (G_LIKELY (length > 0 && contents[length] == '\0'))
+ length = -1;
+
+ /* leave when the contents is not utf-8 valid */
+ if (g_utf8_validate (contents, length, &end) == FALSE)
+ {
+ /* set return value */
+ retval = ERROR_NOT_UTF8_VALID;
+
+ /* set an error */
+ g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
+ _("Invalid byte sequence in conversion input"));
+
+ goto failed;
+ }
+ }
+ else
+ {
+ /* get the encoding charset */
+ charset = mousepad_encoding_get_charset (file->encoding);
+
+ /* convert the contents */
+ encoded = g_convert (contents, length, "UTF-8", charset, NULL, &written, error);
+
+ /* check if the string is utf-8 valid */
+ if (G_UNLIKELY (encoded == NULL))
+ {
+ /* set return value */
+ retval = ERROR_CONVERTING_FAILED;
+
+ goto failed;
+ }
+
+ /* set new values */
+ contents = encoded;
+ length = written;
+
+ /* validate the converted content */
+ goto validate;
+ }
+
+ /* detect the line ending, based on the first eol we match */
+ for (n = contents; n < end; n = g_utf8_next_char (n))
+ {
+ if (G_LIKELY (*n == '\n'))
+ {
+ /* set unix line ending */
+ file->line_ending = MOUSEPAD_EOL_UNIX;
+
+ break;
+ }
+ else if (*n == '\r')
+ {
+ /* get next character */
+ n = g_utf8_next_char (n);
+
+ /* set dos or mac line ending */
+ file->line_ending = (*n == '\n') ? MOUSEPAD_EOL_DOS : MOUSEPAD_EOL_MAC;
+
+ break;
+ }
+ }
+
+ /* get the iter at the beginning of the document */
+ gtk_text_buffer_get_start_iter (file->buffer, &start_iter);
+
+ /* insert the file contents in the buffer (for documents with cr line ending) */
+ for (n = m = contents; n < end; n = g_utf8_next_char (n))
+ {
+ if (G_UNLIKELY (*n == '\r'))
+ {
+ /* insert the text in the buffer */
+ if (G_LIKELY (n - m > 0))
+ gtk_text_buffer_insert (file->buffer, &start_iter, m, n - m);
+
+ /* advance the offset */
+ m = g_utf8_next_char (n);
+
+ /* insert a new line when the document is not cr+lf */
+ if (*m != '\n')
+ gtk_text_buffer_insert (file->buffer, &start_iter, "\n", 1);
+ }
+ }
+
+ /* insert the remaining part, or everything for lf line ending */
+ if (G_LIKELY (n - m > 0))
+ gtk_text_buffer_insert (file->buffer, &start_iter, m, n - m);
+
+ /* get the start iter */
+ gtk_text_buffer_get_start_iter (file->buffer, &start_iter);
+
+ /* set the cursor to the beginning of the document */
+ gtk_text_buffer_place_cursor (file->buffer, &start_iter);
+ }
+
+ /* assume everything when file */
+ retval = 0;
+
+ /* store the file status */
+ if (G_LIKELY (filename != template_filename))
+ {
+ if (G_LIKELY (g_stat (file->filename, &statb) == 0))
+ {
+ /* whether the file is readonly (ie. not writable by the user) */
+ mousepad_file_set_readonly (file, !((statb.st_mode & S_IWUSR) != 0));
+
+ /* store the file modification time */
+ file->mtime = statb.st_mtime;
+ }
+ else
+ {
+ /* set return value */
+ retval = ERROR_FILE_STATUS_FAILED;
+ }
+ }
+ else
+ {
+ /* this is a new document with content from a template */
+ file->mtime = 0;
+ mousepad_file_set_readonly (file, FALSE);
+ }
+
+ failed:
+
+ /* make sure the buffer is empty if we did not succeed */
+ if (G_UNLIKELY (retval != 0))
+ {
+ gtk_text_buffer_get_bounds (file->buffer, &start_iter, &end_iter);
+ gtk_text_buffer_delete (file->buffer, &start_iter, &end_iter);
+ }
+
+ /* cleanup */
+ g_free (encoded);
+
+ /* close the mapped file */
+#if GLIB_CHECK_VERSION (2, 21, 0)
+ g_mapped_file_unref (mapped_file);
+#else
+ g_mapped_file_free (mapped_file);
+#endif
+
+ /* guess and set the file's filetype/language */
+ mousepad_file_set_language (file, mousepad_file_guess_language (file));
+
+ /* this does not count as a modified buffer */
+ gtk_text_buffer_set_modified (file->buffer, FALSE);
+ }
+
+ return retval;
+}
+
+
+
+gboolean
+mousepad_file_save (MousepadFile *file,
+ GError **error)
+{
+ gint fd;
+ gboolean succeed = FALSE;
+ gchar *contents, *p;
+ gchar *encoded;
+ const gchar *charset;
+ GtkTextIter start_iter, end_iter;
+ gint l, m, length;
+ gsize written;
+ struct stat statb;
+ gchar **chunks;
+
+ mousepad_return_val_if_fail (MOUSEPAD_IS_FILE (file), FALSE);
+ mousepad_return_val_if_fail (GTK_IS_TEXT_BUFFER (file->buffer), FALSE);
+ mousepad_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+ mousepad_return_val_if_fail (file->filename != NULL, FALSE);
+
+ /* open the file */
+ fd = g_open (file->filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+ if (G_LIKELY (fd != -1))
+ {
+ /* get the buffer bounds */
+ gtk_text_buffer_get_bounds (file->buffer, &start_iter, &end_iter);
+
+ /* get the buffer contents */
+ contents = gtk_text_buffer_get_slice (file->buffer, &start_iter, &end_iter, TRUE);
+
+ if (G_LIKELY (contents))
+ {
+ /* get the content length */
+ length = strlen (contents);
+
+ /* handle line endings */
+ if (file->line_ending == MOUSEPAD_EOL_MAC)
+ {
+ /* replace the unix with a mac line ending */
+ for (p = contents; *p != '\0'; p++)
+ if (G_UNLIKELY (*p == '\n'))
+ *p = '\r';
+ }
+ else if (file->line_ending == MOUSEPAD_EOL_DOS)
+ {
+ /* split the contents into chunks */
+ chunks = g_strsplit (contents, "\n", -1);
+
+ /* cleanup */
+ g_free (contents);
+
+ /* join the chunks with dos line endings in between */
+ contents = g_strjoinv ("\r\n", chunks);
+
+ /* cleanup */
+ g_strfreev (chunks);
+
+ /* new contents length */
+ length = strlen (contents);
+ }
+
+ /* add and utf-8 bom at the start of the contents if needed */
+ if (file->write_bom && mousepad_encoding_is_unicode (file->encoding))
+ {
+ /* realloc the contents string */
+ contents = g_realloc (contents, length + 4);
+
+ /* move the existing contents 3 bytes */
+ g_memmove (contents + 3, contents, length + 1);
+
+ /* write an utf-8 bom at the start of the contents */
+ contents[0] = (guchar) 0xef;
+ contents[1] = (guchar) 0xbb;
+ contents[2] = (guchar) 0xbf;
+
+ /* increase the length */
+ length += 3;
+ }
+
+ /* convert to the encoding if set */
+ if (G_UNLIKELY (file->encoding != MOUSEPAD_ENCODING_UTF_8))
+ {
+ /* get the charset */
+ charset = mousepad_encoding_get_charset (file->encoding);
+ if (G_UNLIKELY (charset == NULL))
+ goto failed;
+
+ /* convert the content to the user encoding */
+ encoded = g_convert (contents, length, charset, "UTF-8", NULL, &written, error);
+
+ /* return if nothing was encoded */
+ if (G_UNLIKELY (encoded == NULL))
+ goto failed;
+
+ /* cleanup */
+ g_free (contents);
+
+ /* set the new contents */
+ contents = encoded;
+ length = written;
+ }
+
+ /* write the buffer to the file */
+ for (m = 0; m < length;)
+ {
+ /* write */
+ l = write (fd, contents + m, length - m);
+
+ if (G_UNLIKELY (l < 0))
+ {
+ /* just try again on EAGAIN/EINTR */
+ if (G_LIKELY (errno != EAGAIN && errno != EINTR))
+ {
+ /* set an error */
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno), "%s", g_strerror (errno));
+
+ /* bail out */
+ goto failed;
+ }
+ }
+ else
+ {
+ /* advance the offset */
+ m += l;
+ }
+ }
+
+ /* set the new modification time */
+ if (G_LIKELY (fstat (fd, &statb) == 0))
+ file->mtime = statb.st_mtime;
+
+ /* everything has been saved */
+ gtk_text_buffer_set_modified (file->buffer, FALSE);
+
+ /* we saved succesfully */
+ mousepad_file_set_readonly (file, FALSE);
+
+ /* everything went file */
+ succeed = TRUE;
+
+ failed:
+
+ /* cleanup */
+ g_free (contents);
+
+ /* close the file */
+ close (fd);
+ }
+ }
+ else
+ {
+ /* set an error */
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno), "%s", g_strerror (errno));
+ }
+
+ return succeed;
+}
+
+
+
+gboolean
+mousepad_file_reload (MousepadFile *file,
+ GError **error)
+{
+ GtkTextIter start, end;
+ gboolean succeed = FALSE;
+
+ mousepad_return_val_if_fail (MOUSEPAD_IS_FILE (file), FALSE);
+ mousepad_return_val_if_fail (GTK_IS_TEXT_BUFFER (file->buffer), FALSE);
+ mousepad_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+ mousepad_return_val_if_fail (file->filename != NULL, FALSE);
+
+ /* simple test if the file has not been removed */
+ if (G_UNLIKELY (g_file_test (file->filename, G_FILE_TEST_EXISTS) == FALSE))
+ {
+ /* set an error */
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_NOENT,
+ _("The file \"%s\" you tried to reload does not exist anymore"), file->filename);
+
+ return FALSE;
+ }
+
+ /* clear the buffer */
+ gtk_text_buffer_get_bounds (file->buffer, &start, &end);
+ gtk_text_buffer_delete (file->buffer, &start, &end);
+
+ /* reload the file */
+ succeed = mousepad_file_open (file, NULL, error);
+
+ return succeed;
+}
+
+
+
+gboolean
+mousepad_file_get_externally_modified (MousepadFile *file,
+ GError **error)
+{
+ struct stat statb;
+ GFileError error_code;
+
+ mousepad_return_val_if_fail (MOUSEPAD_IS_FILE (file), TRUE);
+ mousepad_return_val_if_fail (file->filename != NULL, TRUE);
+ mousepad_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ /* check if our modification time differs from the current one */
+ if (G_LIKELY (g_stat (file->filename, &statb) == 0))
+ return (file->mtime > 0 && statb.st_mtime != file->mtime);
+
+ /* get the error code */
+ error_code = g_file_error_from_errno (errno);
+
+ /* file does not exists, nothing wrong with that */
+ if (G_LIKELY (error_code == G_FILE_ERROR_NOENT))
+ return FALSE;
+
+ /* set an error */
+ if (error != NULL)
+ g_set_error (error, G_FILE_ERROR, error_code, _("Failed to read the status of \"%s\""), file->filename);
+
+ return TRUE;
+}
diff --git a/mousepad/mousepad-file.h b/mousepad/mousepad-file.h
new file mode 100644
index 0000000..e9a8be3
--- /dev/null
+++ b/mousepad/mousepad-file.h
@@ -0,0 +1,97 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __MOUSEPAD_FILE_H__
+#define __MOUSEPAD_FILE_H__
+
+G_BEGIN_DECLS
+
+#include <mousepad/mousepad-encoding.h>
+
+typedef struct _MousepadFileClass MousepadFileClass;
+typedef struct _MousepadFile MousepadFile;
+typedef enum _MousepadLineEnding MousepadLineEnding;
+
+#define MOUSEPAD_TYPE_FILE (mousepad_file_get_type ())
+#define MOUSEPAD_FILE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MOUSEPAD_TYPE_FILE, MousepadFile))
+#define MOUSEPAD_FILE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MOUSEPAD_TYPE_FILE, MousepadFileClass))
+#define MOUSEPAD_IS_FILE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MOUSEPAD_TYPE_FILE))
+#define MOUSEPAD_IS_FILE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MOUSEPAD_TYPE_FILE))
+#define MOUSEPAD_FILE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MOUSEPAD_TYPE_FILE, MousepadFileClass))
+
+enum _MousepadLineEnding
+{
+ MOUSEPAD_EOL_UNIX,
+ MOUSEPAD_EOL_MAC,
+ MOUSEPAD_EOL_DOS
+};
+
+GType mousepad_file_get_type (void) G_GNUC_CONST;
+
+MousepadFile *mousepad_file_new (GtkTextBuffer *buffer);
+
+void mousepad_file_set_filename (MousepadFile *file,
+ const gchar *filename);
+
+const gchar *mousepad_file_get_filename (MousepadFile *file);
+
+gchar *mousepad_file_get_uri (MousepadFile *file);
+
+void mousepad_file_set_encoding (MousepadFile *file,
+ MousepadEncoding encoding);
+
+MousepadEncoding mousepad_file_get_encoding (MousepadFile *file);
+
+gboolean mousepad_file_get_read_only (MousepadFile *file);
+
+void mousepad_file_set_write_bom (MousepadFile *file,
+ gboolean write_bom);
+
+gboolean mousepad_file_get_write_bom (MousepadFile *file,
+ gboolean *sensitive);
+
+void mousepad_file_set_line_ending (MousepadFile *file,
+ MousepadLineEnding line_ending);
+
+MousepadLineEnding mousepad_file_get_line_ending (MousepadFile *file);
+
+void mousepad_file_set_language (MousepadFile *file,
+ GtkSourceLanguage *language);
+
+GtkSourceLanguage *mousepad_file_get_language (MousepadFile *file);
+
+void mousepad_file_set_language_id (MousepadFile *file,
+ const gchar *language_id);
+
+const gchar *mousepad_file_get_language_id (MousepadFile *file);
+
+GtkSourceLanguage *mousepad_file_guess_language (MousepadFile *file);
+
+gint mousepad_file_open (MousepadFile *file,
+ const gchar *template_filename,
+ GError **error);
+
+gboolean mousepad_file_save (MousepadFile *file,
+ GError **error);
+
+gboolean mousepad_file_reload (MousepadFile *file,
+ GError **error);
+
+gboolean mousepad_file_get_externally_modified (MousepadFile *file,
+ GError **error);
+G_END_DECLS
+
+#endif /* !__MOUSEPAD_FILE_H__ */
diff --git a/mousepad/mousepad-marshal.list b/mousepad/mousepad-marshal.list
new file mode 100644
index 0000000..4b5c216
--- /dev/null
+++ b/mousepad/mousepad-marshal.list
@@ -0,0 +1,3 @@
+VOID:INT,INT,INT
+INT:FLAGS,STRING,STRING
+VOID:OBJECT,INT,INT
diff --git a/mousepad/mousepad-preferences.c b/mousepad/mousepad-preferences.c
new file mode 100644
index 0000000..740b281
--- /dev/null
+++ b/mousepad/mousepad-preferences.c
@@ -0,0 +1,652 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_MEMORY_H
+#include <memory.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <glib-object.h>
+
+#include <mousepad/mousepad-private.h>
+#include <mousepad/mousepad-util.h>
+#include <mousepad/mousepad-preferences.h>
+
+#define GROUP_NAME "Configuration"
+
+
+
+enum
+{
+ PROP_0,
+
+ /* search preferences */
+ PROP_SEARCH_DIRECTION,
+ PROP_SEARCH_MATCH_CASE,
+ PROP_SEARCH_MATCH_WHOLE_WORD,
+ PROP_SEARCH_REPLACE_ALL_LOCATION,
+
+ /* textview preferences */
+ PROP_VIEW_AUTO_INDENT,
+ PROP_VIEW_FONT_NAME,
+ PROP_VIEW_LINE_NUMBERS,
+ PROP_VIEW_TAB_WIDTH,
+ PROP_VIEW_TABS_AS_SPACES,
+ PROP_VIEW_WORD_WRAP,
+ PROP_VIEW_COLOR_SCHEME,
+
+ /* window preferences */
+ PROP_WINDOW_HEIGHT,
+ PROP_WINDOW_WIDTH,
+ PROP_WINDOW_STATUSBAR_VISIBLE,
+
+ /* hidden settings */
+ PROP_MISC_ALWAYS_SHOW_TABS,
+ PROP_MISC_CYCLE_TABS,
+ PROP_MISC_DEFAULT_TAB_SIZES,
+ PROP_MISC_PATH_IN_TITLE,
+ PROP_MISC_RECENT_MENU_ITEMS,
+ PROP_MISC_REMEMBER_GEOMETRY,
+ N_PROPERTIES
+};
+
+
+
+static void mousepad_preferences_finalize (GObject *object);
+static void mousepad_preferences_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void mousepad_preferences_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+#ifndef NDEBUG
+static void mousepad_preferences_check_option_name (GParamSpec *pspec);
+#endif
+static void mousepad_preferences_load (MousepadPreferences *preferences);
+static void mousepad_preferences_store (MousepadPreferences *preferences);
+static gboolean mousepad_preferences_store_idle (gpointer user_data);
+static void mousepad_preferences_store_idle_destroy (gpointer user_data);
+
+struct _MousepadPreferencesClass
+{
+ GObjectClass __parent__;
+};
+
+struct _MousepadPreferences
+{
+ GObject __parent__;
+
+ /* all the properties stored in the object */
+ GValue values[N_PROPERTIES];
+
+ /* idle save timer id */
+ gint store_idle_id;
+};
+
+
+
+G_DEFINE_TYPE (MousepadPreferences, mousepad_preferences, G_TYPE_OBJECT);
+
+
+
+static void
+mousepad_preferences_class_init (MousepadPreferencesClass *klass)
+{
+ GObjectClass *gobject_class;
+
+ gobject_class = G_OBJECT_CLASS (klass);
+ gobject_class->finalize = mousepad_preferences_finalize;
+ gobject_class->get_property = mousepad_preferences_get_property;
+ gobject_class->set_property = mousepad_preferences_set_property;
+
+ /**
+ * Search Preferences
+ **/
+ g_object_class_install_property (gobject_class,
+ PROP_SEARCH_DIRECTION,
+ g_param_spec_int ("search-direction",
+ "SearchDirection",
+ NULL,
+ 0, 2, 1,
+ MOUSEPAD_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class,
+ PROP_SEARCH_MATCH_CASE,
+ g_param_spec_boolean ("search-match-case",
+ "SearchMatchCase",
+ NULL,
+ FALSE,
+ MOUSEPAD_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class,
+ PROP_SEARCH_MATCH_WHOLE_WORD,
+ g_param_spec_boolean ("search-match-whole-word",
+ "SearchMatchWholeWord",
+ NULL,
+ FALSE,
+ MOUSEPAD_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class,
+ PROP_SEARCH_REPLACE_ALL_LOCATION,
+ g_param_spec_int ("search-replace-all-location",
+ "SearchReplaceAllLocation",
+ NULL,
+ 0, 2, 1,
+ MOUSEPAD_PARAM_READWRITE));
+
+ /**
+ * Textview Preferences
+ **/
+ g_object_class_install_property (gobject_class,
+ PROP_VIEW_AUTO_INDENT,
+ g_param_spec_boolean ("view-auto-indent",
+ "ViewAutoIndent",
+ NULL,
+ FALSE,
+ MOUSEPAD_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class,
+ PROP_VIEW_FONT_NAME,
+ g_param_spec_string ("view-font-name",
+ "ViewFontName",
+ NULL,
+ NULL,
+ MOUSEPAD_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class,
+ PROP_VIEW_LINE_NUMBERS,
+ g_param_spec_boolean ("view-line-numbers",
+ "ViewLineNumbers",
+ NULL,
+ FALSE,
+ MOUSEPAD_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class,
+ PROP_VIEW_TAB_WIDTH,
+ g_param_spec_int ("view-tab-size",
+ "ViewTabSize",
+ NULL,
+ 1, 32, 8,
+ MOUSEPAD_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class,
+ PROP_VIEW_TABS_AS_SPACES,
+ g_param_spec_boolean ("view-insert-spaces",
+ "ViewInsertSpaces",
+ NULL,
+ FALSE,
+ MOUSEPAD_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class,
+ PROP_VIEW_WORD_WRAP,
+ g_param_spec_boolean ("view-word-wrap",
+ "ViewWordWrap",
+ NULL,
+ FALSE,
+ MOUSEPAD_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class,
+ PROP_VIEW_COLOR_SCHEME,
+ g_param_spec_string ("view-color-scheme",
+ "ViewColorScheme",
+ NULL,
+ "none",
+ MOUSEPAD_PARAM_READWRITE));
+
+
+ /**
+ * Window Preferences
+ **/
+ g_object_class_install_property (gobject_class,
+ PROP_WINDOW_HEIGHT,
+ g_param_spec_int ("window-height",
+ "WindowHeight",
+ NULL,
+ 1, G_MAXINT, 480,
+ MOUSEPAD_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class,
+ PROP_WINDOW_WIDTH,
+ g_param_spec_int ("window-width",
+ "WindowWidth",
+ NULL,
+ 1, G_MAXINT, 640,
+ MOUSEPAD_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class,
+ PROP_WINDOW_STATUSBAR_VISIBLE,
+ g_param_spec_boolean ("window-statusbar-visible",
+ "WindowStatusbarVisible",
+ NULL,
+ TRUE,
+ MOUSEPAD_PARAM_READWRITE));
+
+
+ /**
+ * Hidden Preferences
+ **/
+ g_object_class_install_property (gobject_class,
+ PROP_MISC_ALWAYS_SHOW_TABS,
+ g_param_spec_boolean ("misc-always-show-tabs",
+ "MiscAlwaysShowTabs",
+ NULL,
+ FALSE,
+ MOUSEPAD_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class,
+ PROP_MISC_CYCLE_TABS,
+ g_param_spec_boolean ("misc-cycle-tabs",
+ "MiscCycleTabs",
+ NULL,
+ FALSE,
+ MOUSEPAD_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class,
+ PROP_MISC_DEFAULT_TAB_SIZES,
+ g_param_spec_string ("misc-default-tab-sizes",
+ "MiscDefaultTabSizes",
+ NULL,
+ "2,3,4,8",
+ MOUSEPAD_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class,
+ PROP_MISC_PATH_IN_TITLE,
+ g_param_spec_boolean ("misc-path-in-title",
+ "MiscPathInTitle",
+ NULL,
+ FALSE,
+ MOUSEPAD_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class,
+ PROP_MISC_RECENT_MENU_ITEMS,
+ g_param_spec_int ("misc-recent-menu-items",
+ "MiscRecentMenuItems",
+ NULL,
+ 1, 100, 10,
+ MOUSEPAD_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class,
+ PROP_MISC_REMEMBER_GEOMETRY,
+ g_param_spec_boolean ("misc-remember-geometry",
+ "MiscRememberGeometry",
+ NULL,
+ TRUE,
+ MOUSEPAD_PARAM_READWRITE));
+}
+
+
+
+static void
+mousepad_preferences_init (MousepadPreferences *preferences)
+{
+ /* load the settings */
+ mousepad_preferences_load (preferences);
+}
+
+
+
+static void
+mousepad_preferences_finalize (GObject *object)
+{
+ MousepadPreferences *preferences = MOUSEPAD_PREFERENCES (object);
+ guint n;
+
+ /* flush preferences */
+ if (G_UNLIKELY (preferences->store_idle_id != 0))
+ {
+ g_source_remove (preferences->store_idle_id);
+ mousepad_preferences_store (preferences);
+ }
+ /* release the property values */
+ for (n = 1; n < N_PROPERTIES; ++n)
+ if (G_IS_VALUE (preferences->values + n))
+ g_value_unset (preferences->values + n);
+
+ (*G_OBJECT_CLASS (mousepad_preferences_parent_class)->finalize) (object);
+}
+
+
+
+static void
+mousepad_preferences_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ MousepadPreferences *preferences = MOUSEPAD_PREFERENCES (object);
+ GValue *src = preferences->values + prop_id;
+
+ if (G_LIKELY (G_IS_VALUE (src)))
+ g_value_copy (src, value);
+ else
+ g_param_value_set_default (pspec, value);
+}
+
+
+
+static void
+mousepad_preferences_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ MousepadPreferences *preferences = MOUSEPAD_PREFERENCES (object);
+ GValue *dst = preferences->values + prop_id;
+
+ /* initialize value if needed */
+ if (G_UNLIKELY (G_IS_VALUE (dst) == FALSE))
+ {
+ g_value_init (dst, pspec->value_type);
+ g_param_value_set_default (pspec, dst);
+ }
+
+ /* compare the values */
+ if (g_param_values_cmp (pspec, value, dst) != 0)
+ {
+ /* copy the new value */
+ g_value_copy (value, dst);
+
+ /* queue a store */
+ mousepad_preferences_store (preferences);
+ }
+}
+
+
+
+#ifndef NDEBUG
+static void
+mousepad_preferences_check_option_name (GParamSpec *pspec)
+{
+ const gchar *name, *nick;
+ gchar *option;
+
+ /* get property name and nick */
+ name = g_param_spec_get_name (pspec);
+ nick = g_param_spec_get_nick (pspec);
+
+ if (G_UNLIKELY (nick == NULL))
+ {
+ g_warning ("The nick name of %s is NULL", name);
+ }
+ else
+ {
+ /* get option name */
+ option = mousepad_util_config_name (name);
+
+ /* compare the strings */
+ if (G_UNLIKELY (!option || strcmp (option, nick) != 0))
+ g_warning ("The option name (%s) and nick name (%s) of property %s do not match", option, nick, name);
+
+ /* cleanup */
+ g_free (option);
+ }
+}
+#endif
+
+
+
+static void
+mousepad_preferences_load (MousepadPreferences *preferences)
+{
+ gchar *string;
+ GParamSpec **pspecs;
+ GParamSpec *pspec;
+ GParamSpecInt *ispec;
+ GKeyFile *keyfile;
+ gchar *filename;
+ GValue *dst;
+ guint nspecs;
+ guint n;
+ gint integer;
+
+ /* get the save location, leave when there if no file found */
+ filename = mousepad_util_get_save_location (MOUSEPAD_RC_RELPATH, FALSE);
+ if (G_UNLIKELY (filename == NULL))
+ return;
+
+ /* create a new key file */
+ keyfile = g_key_file_new ();
+
+ /* open the config file */
+ if (G_LIKELY (g_key_file_load_from_file (keyfile, filename, G_KEY_FILE_NONE, NULL)))
+ {
+ /* freeze notification signals */
+ g_object_freeze_notify (G_OBJECT (preferences));
+
+ /* get all the properties in the class */
+ pspecs = g_object_class_list_properties (G_OBJECT_GET_CLASS (preferences), &nspecs);
+
+ for (n = 0; n < nspecs; n++)
+ {
+ /* get the pspec */
+ pspec = pspecs[n];
+
+ /* get the pspec destination value and initialize it */
+ dst = preferences->values + (n + 1);
+ g_value_init (dst, pspec->value_type);
+
+#ifndef NDEBUG
+ /* check nick name with generated option name */
+ mousepad_preferences_check_option_name (pspec);
+#endif
+
+ /* read the propert value from the key file */
+ string = g_key_file_get_value (keyfile, GROUP_NAME, g_param_spec_get_nick (pspec), NULL);
+ if (G_UNLIKELY (string == NULL || *string == '\0'))
+ goto setdefault;
+
+ if (pspec->value_type == G_TYPE_STRING)
+ {
+ /* set string as the value */
+ g_value_take_string (dst, string);
+
+ /* don't free the string */
+ string = NULL;
+ }
+ else if (pspec->value_type == G_TYPE_INT)
+ {
+ /* get integer spec */
+ ispec = G_PARAM_SPEC_INT (pspec);
+
+ /* get the value and clamp it */
+ integer = CLAMP (atoi (string), ispec->minimum, ispec->maximum);
+
+ /* set the integer */
+ g_value_set_int (dst, integer);
+ }
+ else if (pspec->value_type == G_TYPE_BOOLEAN)
+ {
+ /* set the boolean */
+ g_value_set_boolean (dst, (strcmp (string, "false") != 0));
+ }
+ else
+ {
+ /* print warning */
+ g_warning ("Failed to load property \"%s\"", pspec->name);
+
+ setdefault:
+
+ /* set default */
+ g_param_value_set_default (pspec, dst);
+ }
+
+ /* cleanup */
+ g_free (string);
+ }
+
+ /* cleanup the specs */
+ g_free (pspecs);
+
+ /* allow notifications again */
+ g_object_thaw_notify (G_OBJECT (preferences));
+ }
+
+ /* free the key file */
+ g_key_file_free (keyfile);
+
+ /* cleanup filename */
+ g_free (filename);
+}
+
+
+
+static void
+mousepad_preferences_store (MousepadPreferences *preferences)
+{
+ if (preferences->store_idle_id == 0)
+ {
+ preferences->store_idle_id = g_idle_add_full (G_PRIORITY_LOW, mousepad_preferences_store_idle,
+ preferences, mousepad_preferences_store_idle_destroy);
+ }
+}
+
+
+
+static gboolean
+mousepad_preferences_store_idle (gpointer user_data)
+{
+ MousepadPreferences *preferences = MOUSEPAD_PREFERENCES (user_data);
+ GParamSpec **pspecs;
+ GParamSpec *pspec;
+ GKeyFile *keyfile;
+ gchar *filename;
+ GValue *value;
+ guint nspecs;
+ guint n;
+ const gchar *nick;
+
+ /* get the config filename */
+ filename = mousepad_util_get_save_location (MOUSEPAD_RC_RELPATH, TRUE);
+
+ /* leave when no filename is returned */
+ if (G_UNLIKELY (filename == NULL))
+ return FALSE;
+
+ /* create an empty key file */
+ keyfile = g_key_file_new ();
+
+ /* try to load the file contents, no worries if this fails */
+ g_key_file_load_from_file (keyfile, filename, G_KEY_FILE_NONE, NULL);
+
+ /* get the list of properties in the class */
+ pspecs = g_object_class_list_properties (G_OBJECT_GET_CLASS (preferences), &nspecs);
+
+ for (n = 0; n < nspecs; n++)
+ {
+ /* get the pspec */
+ pspec = pspecs[n];
+
+ /* get the value */
+ value = preferences->values + (n + 1);
+
+ /* continue if the value is not initialized */
+ if (G_UNLIKELY (G_IS_VALUE (value) == FALSE))
+ continue;
+
+ /* get nick name */
+ nick = g_param_spec_get_nick (pspec);
+
+ if (pspec->value_type == G_TYPE_STRING)
+ {
+ /* store the string */
+ if (g_value_get_string (value) != NULL)
+ g_key_file_set_value (keyfile, GROUP_NAME, nick, g_value_get_string (value));
+ }
+ else if (pspec->value_type == G_TYPE_INT)
+ {
+ /* store the interger */
+ g_key_file_set_integer (keyfile, GROUP_NAME, nick, g_value_get_int (value));
+ }
+ else if (pspec->value_type == G_TYPE_BOOLEAN)
+ {
+ /* store the boolean */
+ g_key_file_set_boolean (keyfile, GROUP_NAME, nick, g_value_get_boolean (value));
+ }
+ else
+ {
+ g_warning ("Failed to save property \"%s\"", pspec->name);
+ }
+ }
+
+ /* cleanup the specs */
+ g_free (pspecs);
+
+ /* save the keyfile */
+ mousepad_util_save_key_file (keyfile, filename);
+
+ /* close the key file */
+ g_key_file_free (keyfile);
+
+ /* cleanup */
+ g_free (filename);
+
+ return FALSE;
+}
+
+
+
+static void
+mousepad_preferences_store_idle_destroy (gpointer user_data)
+{
+ MOUSEPAD_PREFERENCES (user_data)->store_idle_id = 0;
+}
+
+
+
+/**
+ * mousepad_preferences_get:
+ *
+ * Queries the global #MousepadPreferences instance, which is shared
+ * by all modules. The function automatically takes a reference
+ * for the caller, so you'll need to call g_object_unref() when
+ * you're done with it.
+ *
+ * Return value: the global #MousepadPreferences instance.
+ **/
+MousepadPreferences*
+mousepad_preferences_get (void)
+{
+ static MousepadPreferences *preferences = NULL;
+
+ if (G_UNLIKELY (preferences == NULL))
+ {
+ preferences = g_object_new (MOUSEPAD_TYPE_PREFERENCES, NULL);
+ g_object_add_weak_pointer (G_OBJECT (preferences), (gpointer) &preferences);
+ }
+ else
+ {
+ g_object_ref (G_OBJECT (preferences));
+ }
+
+ return preferences;
+}
diff --git a/mousepad/mousepad-preferences.h b/mousepad/mousepad-preferences.h
new file mode 100644
index 0000000..31302b7
--- /dev/null
+++ b/mousepad/mousepad-preferences.h
@@ -0,0 +1,38 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __MOUSEPAD_PREFERENCIES_H__
+#define __MOUSEPAD_PREFERENCIES_H__
+
+G_BEGIN_DECLS
+
+#define MOUSEPAD_TYPE_PREFERENCES (mousepad_preferences_get_type ())
+#define MOUSEPAD_PREFERENCES(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MOUSEPAD_TYPE_PREFERENCES, MousepadPreferences))
+#define MOUSEPAD_PREFERENCES_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MOUSEPAD_TYPE_PREFERENCES, MousepadPreferencesClass))
+#define MOUSEPAD_IS_PREFERENCES(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MOUSEPAD_TYPE_PREFERENCES))
+#define MOUSEPAD_IS_PREFERENCES_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MOUSEPAD_TYPE_PREFERENCES))
+#define MOUSEPAD_PREFERENCES_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MOUSEPAD_TYPE_PREFERENCES, MousepadPreferencesClass))
+
+typedef struct _MousepadPreferencesClass MousepadPreferencesClass;
+typedef struct _MousepadPreferences MousepadPreferences;
+
+GType mousepad_preferences_get_type (void) G_GNUC_CONST;
+
+MousepadPreferences *mousepad_preferences_get (void);
+
+G_END_DECLS
+
+#endif /* !__MOUSEPAD_PREFERENCIES_H__ */
diff --git a/mousepad/mousepad-print.c b/mousepad/mousepad-print.c
new file mode 100644
index 0000000..e647ae1
--- /dev/null
+++ b/mousepad/mousepad-print.c
@@ -0,0 +1,805 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pango/pango.h>
+#include <cairo.h>
+
+#include <mousepad/mousepad-private.h>
+#include <mousepad/mousepad-preferences.h>
+#include <mousepad/mousepad-document.h>
+#include <mousepad/mousepad-util.h>
+#include <mousepad/mousepad-print.h>
+
+#define DOCUMENT_SPACING (10)
+
+
+
+static void mousepad_print_finalize (GObject *object);
+static void mousepad_print_settings_load (GtkPrintOperation *operation);
+static void mousepad_print_settings_save_foreach (const gchar *key,
+ const gchar *value,
+ gpointer user_data);
+static void mousepad_print_settings_save (GtkPrintOperation *operation);
+static void mousepad_print_begin_print (GtkPrintOperation *operation,
+ GtkPrintContext *context);
+static void mousepad_print_draw_page (GtkPrintOperation *operation,
+ GtkPrintContext *context,
+ gint page_nr);
+static void mousepad_print_page_setup_dialog (GtkWidget *button,
+ GtkPrintOperation *operation);
+static void mousepad_print_button_toggled (GtkWidget *button,
+ MousepadPrint *print);
+static void mousepad_print_button_font_set (GtkFontButton *button,
+ MousepadPrint *print);
+static PangoAttrList *mousepad_print_attr_list_bold (void);
+static GtkWidget *mousepad_print_create_custom_widget (GtkPrintOperation *operation);
+static void mousepad_print_status_changed (GtkPrintOperation *operation);
+static void mousepad_print_done (GtkPrintOperation *operation,
+ GtkPrintOperationResult result);
+
+
+
+struct _MousepadPrintClass
+{
+ GtkPrintOperationClass __parent__;
+};
+
+struct _MousepadPrint
+{
+ GtkPrintOperation __parent__;
+
+ /* the document we're going to print */
+ MousepadDocument *document;
+
+ /* print dialog widgets */
+ GtkWidget *widget_page_headers;
+ GtkWidget *widget_page_footers;
+ GtkWidget *widget_line_numbers;
+ GtkWidget *widget_text_wrapping;
+ GtkWidget *widget_syntax_highlighting;
+ GtkWidget *widget_header_font;
+ GtkWidget *widget_line_numbers_font;
+ GtkWidget *widget_body_font;
+ GtkWidget *widget_line_numbers_spin;
+ GtkWidget *widget_line_numbers_hbox;
+
+ /* settings */
+ gboolean print_line_numbers;
+ gint line_number_increment;
+
+ /* source view print compositor */
+ GtkSourcePrintCompositor *compositor;
+};
+
+
+
+G_DEFINE_TYPE (MousepadPrint, mousepad_print, GTK_TYPE_PRINT_OPERATION);
+
+
+
+static void
+mousepad_print_class_init (MousepadPrintClass *klass)
+{
+ GObjectClass *gobject_class;
+ GtkPrintOperationClass *gtkprintoperation_class;
+
+ gobject_class = G_OBJECT_CLASS (klass);
+ gobject_class->finalize = mousepad_print_finalize;
+
+ gtkprintoperation_class = GTK_PRINT_OPERATION_CLASS (klass);
+ gtkprintoperation_class->begin_print = mousepad_print_begin_print;
+ gtkprintoperation_class->draw_page = mousepad_print_draw_page;
+ gtkprintoperation_class->create_custom_widget = mousepad_print_create_custom_widget;
+ gtkprintoperation_class->status_changed = mousepad_print_status_changed;
+ gtkprintoperation_class->done = mousepad_print_done;
+}
+
+
+
+static void
+mousepad_print_init (MousepadPrint *print)
+{
+ /* init */
+ print->print_line_numbers = FALSE;
+ print->line_number_increment = 1;
+ print->compositor = NULL;
+
+ /* set a custom tab label */
+ gtk_print_operation_set_custom_tab_label (GTK_PRINT_OPERATION (print), _("Document Settings"));
+}
+
+
+
+static void
+mousepad_print_finalize (GObject *object)
+{
+ MousepadPrint *print = MOUSEPAD_PRINT (object);
+
+ /* cleanup */
+ g_object_unref (print->compositor);
+
+ (*G_OBJECT_CLASS (mousepad_print_parent_class)->finalize) (object);
+}
+
+
+
+static void
+mousepad_print_settings_load (GtkPrintOperation *operation)
+{
+ MousepadPrint *print = MOUSEPAD_PRINT (operation);
+ GKeyFile *keyfile;
+ gchar *filename;
+ GtkPrintSettings *settings = NULL;
+ gchar **keys;
+ gint i;
+ gchar *key;
+ gchar *value;
+ gchar *body_font = NULL;
+ gchar *header_font = NULL;
+ gchar *line_numbers_font = NULL;
+ GtkPageSetup *page_setup;
+ GtkPaperSize *paper_size;
+ PangoContext *context;
+ PangoFontDescription *font_desc;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (print->document));
+ mousepad_return_if_fail (GTK_IS_WIDGET (print->document->textview));
+
+ /* get the config file filename */
+ filename = mousepad_util_get_save_location (MOUSEPAD_RC_RELPATH, FALSE);
+ if (G_UNLIKELY (filename == NULL))
+ return;
+
+ /* create a new keyfile */
+ keyfile = g_key_file_new ();
+
+ if (G_LIKELY (g_key_file_load_from_file (keyfile, filename, G_KEY_FILE_NONE, NULL)))
+ {
+ /* get all the keys from the config file */
+ keys = g_key_file_get_keys (keyfile, "Print Settings", NULL, NULL);
+
+ if (G_LIKELY (keys))
+ {
+ /* create new settings object */
+ settings = gtk_print_settings_new ();
+
+ /* set all the keys */
+ for (i = 0; keys[i] != NULL; i++)
+ {
+ /* read the value from the config file */
+ value = g_key_file_get_value (keyfile, "Print Settings", keys[i], NULL);
+
+ /* set the value */
+ if (G_LIKELY (value))
+ {
+ key = mousepad_util_key_name (keys[i]);
+ gtk_print_settings_set (settings, key, value);
+ g_free (key);
+ g_free (value);
+ }
+ }
+
+ /* cleanup */
+ g_strfreev (keys);
+ }
+ }
+
+ /* free the key file */
+ g_key_file_free (keyfile);
+
+ /* cleanup */
+ g_free (filename);
+
+ if (G_LIKELY (settings))
+ {
+ /* apply the settings */
+ gtk_print_operation_set_print_settings (operation, settings);
+
+ if (gtk_print_settings_get_bool (settings, "page-setup-saved") == TRUE)
+ {
+ /* create new page setup */
+ page_setup = gtk_page_setup_new ();
+
+ /* set orientation */
+ gtk_page_setup_set_orientation (page_setup, gtk_print_settings_get_orientation (settings));
+
+ /* restore margins */
+ gtk_page_setup_set_top_margin (page_setup, gtk_print_settings_get_double (settings, "top-margin"), GTK_UNIT_MM);
+ gtk_page_setup_set_bottom_margin (page_setup, gtk_print_settings_get_double (settings, "bottom-margin"), GTK_UNIT_MM);
+ gtk_page_setup_set_right_margin (page_setup, gtk_print_settings_get_double (settings, "right-margin"), GTK_UNIT_MM);
+ gtk_page_setup_set_left_margin (page_setup, gtk_print_settings_get_double (settings, "left-margin"), GTK_UNIT_MM);
+
+ /* set paper size */
+ paper_size = gtk_print_settings_get_paper_size (settings);
+ if (G_LIKELY (paper_size))
+ gtk_page_setup_set_paper_size (page_setup, paper_size);
+
+ /* set the default page setup */
+ gtk_print_operation_set_default_page_setup (operation, page_setup);
+
+ /* release reference */
+ g_object_unref (G_OBJECT (page_setup));
+ }
+
+ /* restore print settings */
+ g_object_set (print->compositor,
+ "print-header",
+ gtk_print_settings_get_bool (settings, "print-header"),
+ "print-line-numbers",
+ gtk_print_settings_get_int (settings, "line-numbers-increment"),
+ "wrap-mode",
+ gtk_print_settings_get_bool (settings, "text-wrapping") ? GTK_WRAP_WORD : GTK_WRAP_NONE,
+ "highlight-syntax",
+ gtk_print_settings_get_bool (settings, "highlight-syntax"),
+ NULL);
+
+ print->print_line_numbers = gtk_print_settings_get_bool (settings, "print-line-numbers");
+ print->line_number_increment = gtk_print_settings_get_int (settings, "line-numbers-increment");
+
+ /* get the saved fonts, if set */
+ body_font = g_strdup (gtk_print_settings_get (settings, "body-font-name"));
+ header_font = g_strdup (gtk_print_settings_get (settings, "header-font-name"));
+ line_numbers_font = g_strdup (gtk_print_settings_get (settings, "line-numbers-font-name"));
+
+ /* release reference */
+ g_object_unref (G_OBJECT (settings));
+ }
+
+ /* if no font name is set, get the one used in the widget */
+ if (G_UNLIKELY (body_font == NULL))
+ {
+ /* get the font description from the context and convert it into a string */
+ context = gtk_widget_get_pango_context (GTK_WIDGET (print->document->textview));
+ font_desc = pango_context_get_font_description (context);
+ body_font = pango_font_description_to_string (font_desc);
+ }
+
+ /* set the restored body font or the one from the textview */
+ gtk_source_print_compositor_set_body_font_name (print->compositor, body_font);
+
+ /* if header font restored use it, otherwise use body font */
+ if (header_font)
+ gtk_source_print_compositor_set_header_font_name (print->compositor, header_font);
+ else
+ gtk_source_print_compositor_set_header_font_name (print->compositor, body_font);
+
+ /* if line numbers font restored use it, otherwise use body font */
+ if (line_numbers_font)
+ gtk_source_print_compositor_set_line_numbers_font_name (print->compositor, line_numbers_font);
+ else
+ gtk_source_print_compositor_set_line_numbers_font_name (print->compositor, body_font);
+
+ /* cleanup */
+ g_free (body_font);
+ g_free (header_font);
+ g_free (line_numbers_font);
+}
+
+
+
+static void
+mousepad_print_settings_save_foreach (const gchar *key,
+ const gchar *value,
+ gpointer user_data)
+{
+ GKeyFile *keyfile = user_data;
+ gchar *config;
+
+ /* save the setting */
+ if (G_LIKELY (key && value))
+ {
+ config = mousepad_util_config_name (key);
+ g_key_file_set_value (keyfile, "Print Settings", config, value);
+ g_free (config);
+ }
+}
+
+
+
+static void
+mousepad_print_settings_save (GtkPrintOperation *operation)
+{
+ MousepadPrint *print = MOUSEPAD_PRINT (operation);
+ GKeyFile *keyfile;
+ gchar *filename;
+ GtkPrintSettings *settings;
+ GtkPageSetup *page_setup;
+ GtkPaperSize *paper_size;
+
+ /* get the save location */
+ filename = mousepad_util_get_save_location (MOUSEPAD_RC_RELPATH, TRUE);
+
+ /* create a new keyfile */
+ keyfile = g_key_file_new ();
+
+ /* load the existing settings */
+ if (G_LIKELY (g_key_file_load_from_file (keyfile, filename, G_KEY_FILE_NONE, NULL)))
+ {
+ /* get the print settings */
+ settings = gtk_print_operation_get_print_settings (operation);
+
+ if (G_LIKELY (settings != NULL))
+ {
+ /* get the page setup */
+ page_setup = gtk_print_operation_get_default_page_setup (operation);
+
+ /* restore the page setup */
+ if (G_LIKELY (page_setup != NULL))
+ {
+ /* the the settings page orienation */
+ gtk_print_settings_set_orientation (settings, gtk_page_setup_get_orientation (page_setup));
+
+ /* save margins */
+ gtk_print_settings_set_double (settings, "top-margin", gtk_page_setup_get_top_margin (page_setup, GTK_UNIT_MM));
+ gtk_print_settings_set_double (settings, "bottom-margin", gtk_page_setup_get_bottom_margin (page_setup, GTK_UNIT_MM));
+ gtk_print_settings_set_double (settings, "right-margin", gtk_page_setup_get_right_margin (page_setup, GTK_UNIT_MM));
+ gtk_print_settings_set_double (settings, "left-margin", gtk_page_setup_get_left_margin (page_setup, GTK_UNIT_MM));
+
+ /* get the paper size */
+ paper_size = gtk_page_setup_get_paper_size (page_setup);
+
+ /* set settings page size */
+ if (G_LIKELY (paper_size))
+ gtk_print_settings_set_paper_size (settings, paper_size);
+ }
+
+ /* a bool we use for loading */
+ gtk_print_settings_set_bool (settings, "page-setup-saved", page_setup != NULL);
+
+ /* set print settings */
+ gtk_print_settings_set_bool (settings,
+ "print-header",
+ gtk_source_print_compositor_get_print_header (print->compositor));
+
+ gtk_print_settings_set_bool (settings,
+ "print-line-numbers",
+ print->print_line_numbers);
+
+ gtk_print_settings_set_int (settings,
+ "line-numbers-increment",
+ print->line_number_increment);
+
+ gtk_print_settings_set_bool (settings,
+ "text-wrapping",
+ gtk_source_print_compositor_get_wrap_mode (print->compositor) == GTK_WRAP_NONE ? FALSE : TRUE);
+
+ gtk_print_settings_set_bool (settings,
+ "highlight-syntax",
+ gtk_source_print_compositor_get_highlight_syntax (print->compositor));
+
+ gtk_print_settings_set (settings,
+ "body-font-name",
+ gtk_source_print_compositor_get_body_font_name (print->compositor));
+
+ gtk_print_settings_set (settings,
+ "header-font-name",
+ gtk_source_print_compositor_get_header_font_name (print->compositor));
+
+ gtk_print_settings_set (settings,
+ "line-numbers-font-name",
+ gtk_source_print_compositor_get_line_numbers_font_name (print->compositor));
+
+ /* store all the print settings */
+ gtk_print_settings_foreach (settings, mousepad_print_settings_save_foreach, keyfile);
+
+ /* save the contents */
+ mousepad_util_save_key_file (keyfile, filename);
+ }
+ }
+
+ /* cleanup */
+ g_key_file_free (keyfile);
+ g_free (filename);
+}
+
+
+
+static void
+mousepad_print_begin_print (GtkPrintOperation *operation,
+ GtkPrintContext *context)
+{
+ MousepadPrint *print = MOUSEPAD_PRINT (operation);
+ MousepadDocument *document = print->document;
+ gint n_pages = 1;
+ const gchar *file_name;
+
+ /* print header */
+ if (gtk_source_print_compositor_get_print_header (print->compositor))
+ {
+ if (mousepad_document_get_filename (document))
+ file_name = mousepad_document_get_filename (document);
+ else
+ file_name = mousepad_document_get_basename (document);
+
+ gtk_source_print_compositor_set_header_format (print->compositor,
+ TRUE,
+ file_name,
+ NULL,
+ "Page %N of %Q");
+ }
+
+ /* paginate all of the pages at once */
+ while (!gtk_source_print_compositor_paginate (print->compositor, context))
+ ;
+
+ n_pages = gtk_source_print_compositor_get_n_pages (print->compositor);
+
+ /* set the number of pages we're going to draw */
+ gtk_print_operation_set_n_pages (operation, n_pages);
+}
+
+
+
+static void
+mousepad_print_draw_page (GtkPrintOperation *operation,
+ GtkPrintContext *context,
+ gint page_nr)
+{
+ MousepadPrint *print = MOUSEPAD_PRINT (operation);
+
+ gtk_source_print_compositor_draw_page (print->compositor, context, page_nr);
+}
+
+
+
+static void
+mousepad_print_page_setup_dialog (GtkWidget *button,
+ GtkPrintOperation *operation)
+{
+ GtkWidget *toplevel;
+ GtkPrintSettings *settings;
+ GtkPageSetup *page_setup;
+
+ /* get the toplevel of the button */
+ toplevel = gtk_widget_get_toplevel (button);
+ if (G_UNLIKELY (!GTK_WIDGET_TOPLEVEL (toplevel)))
+ toplevel = NULL;
+
+ /* get the print settings */
+ settings = gtk_print_operation_get_print_settings (operation);
+ if (G_UNLIKELY (settings == NULL))
+ settings = gtk_print_settings_new ();
+
+ /* get the page setup */
+ page_setup = gtk_print_operation_get_default_page_setup (operation);
+
+ /* run the dialog */
+ page_setup = gtk_print_run_page_setup_dialog (GTK_WINDOW (toplevel), page_setup, settings);
+
+ /* set the new page setup */
+ gtk_print_operation_set_default_page_setup (operation, page_setup);
+}
+
+
+
+static void
+mousepad_print_button_toggled (GtkWidget *button,
+ MousepadPrint *print)
+{
+ gboolean active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button));
+
+ /* save the correct setting */
+ if (button == print->widget_page_headers)
+ gtk_source_print_compositor_set_print_header (print->compositor, active);
+ else if (button == print->widget_line_numbers)
+ {
+ print->print_line_numbers = active;
+ gtk_widget_set_sensitive (print->widget_line_numbers_hbox, active);
+ }
+ else if (button == print->widget_text_wrapping)
+ gtk_source_print_compositor_set_wrap_mode (print->compositor, active ? GTK_WRAP_WORD : GTK_WRAP_NONE);
+ else if (button == print->widget_syntax_highlighting)
+ gtk_source_print_compositor_set_highlight_syntax (print->compositor, active);
+}
+
+
+
+static void
+mousepad_print_button_font_set (GtkFontButton *button,
+ MousepadPrint *print)
+{
+ const gchar *font;
+ GtkWidget *widget = GTK_WIDGET (button);
+
+ font = gtk_font_button_get_font_name (button);
+
+ if (widget == print->widget_body_font)
+ gtk_source_print_compositor_set_body_font_name (print->compositor, font);
+ else if (widget == print->widget_header_font)
+ gtk_source_print_compositor_set_header_font_name (print->compositor, font);
+ else if (widget == print->widget_line_numbers_font)
+ gtk_source_print_compositor_set_line_numbers_font_name (print->compositor, font);
+}
+
+
+
+static void
+mousepad_print_spin_value_changed (GtkSpinButton *button,
+ MousepadPrint *print)
+{
+ print->line_number_increment = gtk_spin_button_get_value_as_int (button);
+
+ if (print->line_number_increment > 0 && print->print_line_numbers)
+ {
+ gtk_source_print_compositor_set_print_line_numbers (print->compositor,
+ print->line_number_increment);
+ }
+ else
+ gtk_source_print_compositor_set_print_line_numbers (print->compositor, 0);
+}
+
+
+
+static PangoAttrList *
+mousepad_print_attr_list_bold (void)
+{
+ static PangoAttrList *attr_list = NULL;
+ PangoAttribute *attr;
+
+ if (G_UNLIKELY (attr_list == NULL))
+ {
+ /* create new attributes list */
+ attr_list = pango_attr_list_new ();
+
+ /* create attribute */
+ attr = pango_attr_weight_new (PANGO_WEIGHT_BOLD);
+ attr->start_index = 0;
+ attr->end_index = -1;
+
+ /* insert bold element */
+ pango_attr_list_insert (attr_list, attr);
+ }
+
+ return attr_list;
+}
+
+
+
+static GtkWidget *
+mousepad_print_create_custom_widget (GtkPrintOperation *operation)
+{
+ MousepadPrint *print = MOUSEPAD_PRINT (operation);
+ GtkWidget *button;
+ GtkWidget *vbox, *vbox2;
+ GtkWidget *frame;
+ GtkWidget *alignment;
+ GtkWidget *label;
+ GtkWidget *table;
+ GtkAdjustment *adjustment;
+
+ vbox = gtk_vbox_new (FALSE, 6);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
+
+ frame = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE);
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ label = gtk_label_new (_("Page Setup"));
+ gtk_label_set_attributes (GTK_LABEL (label), mousepad_print_attr_list_bold ());
+ gtk_frame_set_label_widget (GTK_FRAME (frame), label);
+ gtk_widget_show (label);
+
+ alignment = gtk_alignment_new (0.0, 0.5, 0.0, 1.0);
+ gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 6, 6, 12, 6);
+ gtk_container_add (GTK_CONTAINER (frame), alignment);
+ gtk_widget_show (alignment);
+
+ button = mousepad_util_image_button (GTK_STOCK_PROPERTIES, _("_Adjust page size and orientation"));
+ g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (mousepad_print_page_setup_dialog), operation);
+ gtk_container_add (GTK_CONTAINER (alignment), button);
+ gtk_widget_show (button);
+
+ frame = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE);
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ label = gtk_label_new (_("Appearance"));
+ gtk_label_set_attributes (GTK_LABEL (label), mousepad_print_attr_list_bold ());
+ gtk_frame_set_label_widget (GTK_FRAME (frame), label);
+ gtk_widget_show (label);
+
+ alignment = gtk_alignment_new (0.5, 0.5, 1.0, 1.0);
+ gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 6, 6, 12, 6);
+ gtk_container_add (GTK_CONTAINER (frame), alignment);
+ gtk_widget_show (alignment);
+
+ vbox2 = gtk_vbox_new (FALSE, 6);
+ gtk_container_add (GTK_CONTAINER (alignment), vbox2);
+ gtk_widget_show (vbox2);
+
+ button = print->widget_page_headers = gtk_check_button_new_with_mnemonic (_("Print page _headers"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
+ gtk_source_print_compositor_get_print_header (print->compositor));
+ g_signal_connect (G_OBJECT (button), "toggled", G_CALLBACK (mousepad_print_button_toggled), print);
+ gtk_box_pack_start (GTK_BOX (vbox2), button, FALSE, FALSE, 0);
+ gtk_widget_show (button);
+
+ button = print->widget_line_numbers = gtk_check_button_new_with_mnemonic (_("Print _line numbers"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
+ print->print_line_numbers);
+ g_signal_connect (G_OBJECT (button), "toggled", G_CALLBACK (mousepad_print_button_toggled), print);
+ gtk_box_pack_start (GTK_BOX (vbox2), button, FALSE, FALSE, 0);
+ gtk_widget_show (button);
+
+ alignment = gtk_alignment_new (0.0, 0.5, 0.0, 1.0);
+ gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 0, 0, 24, 0);
+ gtk_box_pack_start (GTK_BOX (vbox2), alignment, FALSE, FALSE, 0);
+ gtk_widget_show (alignment);
+
+ print->widget_line_numbers_hbox = gtk_hbox_new (FALSE, 6);
+ gtk_widget_set_sensitive (print->widget_line_numbers_hbox, print->print_line_numbers);
+ gtk_container_add (GTK_CONTAINER (alignment), print->widget_line_numbers_hbox);
+ gtk_widget_show (print->widget_line_numbers_hbox);
+
+ label = gtk_label_new (_("Number every"));
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+ gtk_box_pack_start (GTK_BOX (print->widget_line_numbers_hbox), label, FALSE, TRUE, 0);
+ gtk_widget_show (label);
+
+ adjustment = GTK_ADJUSTMENT (gtk_adjustment_new (1.0, 1.0, 100.0, 1.0, 0.0, 0.0));
+ print->widget_line_numbers_spin = gtk_spin_button_new (adjustment, 1.0, 0);
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (print->widget_line_numbers_spin),
+ (gdouble) print->line_number_increment);
+ g_signal_connect (G_OBJECT (print->widget_line_numbers_spin),
+ "value-changed",
+ G_CALLBACK (mousepad_print_spin_value_changed),
+ print);
+ gtk_box_pack_start (GTK_BOX (print->widget_line_numbers_hbox), print->widget_line_numbers_spin, FALSE, TRUE, 0);
+ gtk_widget_show (print->widget_line_numbers_spin);
+
+ label = gtk_label_new (_("line(s)"));
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+ gtk_box_pack_start (GTK_BOX (print->widget_line_numbers_hbox), label, FALSE, TRUE, 0);
+ gtk_widget_show (label);
+
+ button = print->widget_text_wrapping = gtk_check_button_new_with_mnemonic (_("Enable text _wrapping"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
+ gtk_source_print_compositor_get_wrap_mode (print->compositor) == GTK_WRAP_NONE ? FALSE : TRUE);
+ g_signal_connect (G_OBJECT (button), "toggled", G_CALLBACK (mousepad_print_button_toggled), print);
+ gtk_box_pack_start (GTK_BOX (vbox2), button, FALSE, FALSE, 0);
+ gtk_widget_show (button);
+
+ button = print->widget_syntax_highlighting = gtk_check_button_new_with_mnemonic (_("Enable _syntax highlighting"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
+ gtk_source_print_compositor_get_highlight_syntax (print->compositor));
+ g_signal_connect (G_OBJECT (button), "toggled", G_CALLBACK (mousepad_print_button_toggled), print);
+ gtk_box_pack_start (GTK_BOX (vbox2), button, FALSE, FALSE, 0);
+ gtk_widget_show (button);
+
+ frame = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE);
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ label = gtk_label_new (_("Fonts"));
+ gtk_label_set_attributes (GTK_LABEL (label), mousepad_print_attr_list_bold ());
+ gtk_frame_set_label_widget (GTK_FRAME (frame), label);
+ gtk_widget_show (label);
+
+ alignment = gtk_alignment_new (0.0, 0.5, 0.0, 1.0);
+ gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 6, 6, 12, 6);
+ gtk_container_add (GTK_CONTAINER (frame), alignment);
+ gtk_widget_show (alignment);
+
+ table = gtk_table_new (3, 2, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_container_add (GTK_CONTAINER (alignment), table);
+ gtk_widget_show (table);
+
+ label = gtk_label_new (_("Header:"));
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+ gtk_table_attach_defaults (GTK_TABLE (table), label, 0, 1, 0, 1);
+ gtk_widget_show (label);
+
+ print->widget_header_font = gtk_font_button_new_with_font (gtk_source_print_compositor_get_header_font_name (print->compositor));
+ gtk_table_attach_defaults (GTK_TABLE (table), print->widget_header_font, 1, 2, 0, 1);
+ g_signal_connect (G_OBJECT (print->widget_header_font), "font-set", G_CALLBACK (mousepad_print_button_font_set), print);
+ gtk_widget_show (print->widget_header_font);
+
+ label = gtk_label_new (_("Body:"));
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+ gtk_table_attach_defaults (GTK_TABLE (table), label, 0, 1, 1, 2);
+ gtk_widget_show (label);
+
+ print->widget_body_font = gtk_font_button_new_with_font (gtk_source_print_compositor_get_body_font_name (print->compositor));
+ gtk_table_attach_defaults (GTK_TABLE (table), print->widget_body_font, 1, 2, 1, 2);
+ g_signal_connect (G_OBJECT (print->widget_body_font), "font-set", G_CALLBACK (mousepad_print_button_font_set), print);
+ gtk_widget_show (print->widget_body_font);
+
+ label = gtk_label_new (_("Line numbers:"));
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+ gtk_table_attach_defaults (GTK_TABLE (table), label, 0, 1, 2, 3);
+ gtk_widget_show (label);
+
+ print->widget_line_numbers_font = gtk_font_button_new_with_font (gtk_source_print_compositor_get_line_numbers_font_name (print->compositor));
+ gtk_table_attach_defaults (GTK_TABLE (table), print->widget_line_numbers_font, 1, 2, 2, 3);
+ g_signal_connect (G_OBJECT (print->widget_line_numbers_font), "font-set", G_CALLBACK (mousepad_print_button_font_set), print);
+ gtk_widget_show (print->widget_line_numbers_font);
+
+ return vbox;
+}
+
+
+
+static void
+mousepad_print_status_changed (GtkPrintOperation *operation)
+{
+ /* nothing usefull for now, we could set a statusbar text or something */
+}
+
+
+
+static void
+mousepad_print_done (GtkPrintOperation *operation,
+ GtkPrintOperationResult result)
+{
+ /* check if the print succeeded */
+ if (result == GTK_PRINT_OPERATION_RESULT_APPLY)
+ {
+ /* save the settings */
+ mousepad_print_settings_save (operation);
+ }
+}
+
+
+
+MousepadPrint *
+mousepad_print_new (void)
+{
+ return g_object_new (MOUSEPAD_TYPE_PRINT, NULL);
+}
+
+
+
+gboolean
+mousepad_print_document_interactive (MousepadPrint *print,
+ MousepadDocument *document,
+ GtkWindow *parent,
+ GError **error)
+{
+ GtkPrintOperationResult result;
+
+ mousepad_return_val_if_fail (MOUSEPAD_IS_PRINT (print), FALSE);
+ mousepad_return_val_if_fail (GTK_IS_PRINT_OPERATION (print), FALSE);
+ mousepad_return_val_if_fail (MOUSEPAD_IS_DOCUMENT (document), FALSE);
+ mousepad_return_val_if_fail (GTK_IS_SOURCE_BUFFER (document->buffer), FALSE);
+ mousepad_return_val_if_fail (GTK_IS_WINDOW (parent), FALSE);
+ mousepad_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ /* set the document */
+ print->document = document;
+ print->compositor = gtk_source_print_compositor_new (GTK_SOURCE_BUFFER (document->buffer));
+
+ /* load settings */
+ mousepad_print_settings_load (GTK_PRINT_OPERATION (print));
+
+ /* allow async printing is support by the platform */
+ gtk_print_operation_set_allow_async (GTK_PRINT_OPERATION (print), TRUE);
+
+ /* run the operation */
+ result = gtk_print_operation_run (GTK_PRINT_OPERATION (print),
+ GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG,
+ parent, error);
+
+ return (result != GTK_PRINT_OPERATION_RESULT_ERROR);
+}
diff --git a/mousepad/mousepad-print.h b/mousepad/mousepad-print.h
new file mode 100644
index 0000000..3806d7b
--- /dev/null
+++ b/mousepad/mousepad-print.h
@@ -0,0 +1,43 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __MOUSEPAD_PRINT_H__
+#define __MOUSEPAD_PRINT_H__
+
+G_BEGIN_DECLS
+
+typedef struct _MousepadPrintClass MousepadPrintClass;
+typedef struct _MousepadPrint MousepadPrint;
+
+#define MOUSEPAD_TYPE_PRINT (mousepad_print_get_type ())
+#define MOUSEPAD_PRINT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MOUSEPAD_TYPE_PRINT, MousepadPrint))
+#define MOUSEPAD_PRINT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MOUSEPAD_TYPE_PRINT, MousepadPrintClass))
+#define MOUSEPAD_IS_PRINT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MOUSEPAD_TYPE_PRINT))
+#define MOUSEPAD_IS_PRINT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MOUSEPAD_TYPE_PRINT))
+#define MOUSEPAD_PRINT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MOUSEPAD_TYPE_PRINT, MousepadPrintClass))
+
+GType mousepad_print_get_type (void) G_GNUC_CONST;
+
+MousepadPrint *mousepad_print_new (void);
+
+gboolean mousepad_print_document_interactive (MousepadPrint *print,
+ MousepadDocument *document,
+ GtkWindow *parent,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* !__MOUSEPAD_PRINT_H__ */
diff --git a/mousepad/mousepad-private.h b/mousepad/mousepad-private.h
new file mode 100644
index 0000000..49ec851
--- /dev/null
+++ b/mousepad/mousepad-private.h
@@ -0,0 +1,126 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __MOUSEPAD_PRIVATE_H__
+#define __MOUSEPAD_PRIVATE_H__
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+#include <gtksourceview/gtksourceview.h>
+#include <gtksourceview/gtksourcestylescheme.h>
+#include <gtksourceview/gtksourcestyleschememanager.h>
+#include <gtksourceview/gtksourcelanguage.h>
+#include <gtksourceview/gtksourcelanguagemanager.h>
+#include <gtksourceview/gtksourceprintcompositor.h>
+
+G_BEGIN_DECLS
+
+/* errors inside mousepad */
+enum
+{
+ ERROR_READING_FAILED = -1,
+ ERROR_CONVERTING_FAILED = -2,
+ ERROR_NOT_UTF8_VALID = -3,
+ ERROR_FILE_STATUS_FAILED = -4
+};
+
+/* config file locations */
+#define MOUSEPAD_RC_RELPATH ("Mousepad" G_DIR_SEPARATOR_S "mousepadrc")
+#define MOUSEPAD_ACCELS_RELPATH ("Mousepad" G_DIR_SEPARATOR_S "accels.scm")
+
+/* handling flags */
+#define MOUSEPAD_SET_FLAG(flags,flag) G_STMT_START{ ((flags) |= (flag)); }G_STMT_END
+#define MOUSEPAD_UNSET_FLAG(flags,flag) G_STMT_START{ ((flags) &= ~(flag)); }G_STMT_END
+#define MOUSEPAD_HAS_FLAG(flags,flag) (((flags) & (flag)) != 0)
+
+/* for personal testing */
+#define TIMER_START GTimer *__FUNCTION__timer = g_timer_new();
+#define TIMER_SPLIT g_print ("%s: %.2f ms\n", G_STRLOC, g_timer_elapsed (__FUNCTION__timer, NULL) * 1000);
+#define TIMER_STOP TIMER_SPLIT g_timer_destroy (__FUNCTION__timer);
+#define PRINT_LOCATION g_print ("%s\n", G_STRLOC);
+
+/* optimize the properties */
+#define MOUSEPAD_PARAM_READWRITE (G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)
+
+/* support for canonical strings and quarks */
+#define I_(string) (g_intern_static_string (string))
+
+/* convienient function for setting object data */
+#define mousepad_object_set_data(object,key,data) (g_object_set_qdata ((object), \
+ g_quark_from_static_string (key), (data)))
+#define mousepad_object_set_data_full(object,key,data,destroy) (g_object_set_qdata_full ((object), \
+ g_quark_from_static_string (key), (data), (GDestroyNotify) (destroy)))
+#define mousepad_object_get_data(object,key) (g_object_get_qdata ((object), g_quark_try_string (key)))
+
+/* support macros for debugging */
+#ifndef NDEBUG
+#define mousepad_assert(expr) g_assert (expr)
+#define mousepad_assert_not_reached() g_assert_not_reached ()
+#define mousepad_return_if_fail(expr) g_return_if_fail (expr)
+#define mousepad_return_val_if_fail(expr, val) g_return_val_if_fail (expr, (val))
+#else
+#define mousepad_assert(expr) G_STMT_START{ (void)0; }G_STMT_END
+#define mousepad_assert_not_reached() G_STMT_START{ (void)0; }G_STMT_END
+#define mousepad_return_if_fail(expr) G_STMT_START{ (void)0; }G_STMT_END
+#define mousepad_return_val_if_fail(expr, val) G_STMT_START{ (void)0; }G_STMT_END
+#endif
+
+/* avoid trivial g_value_get_*() function calls */
+#ifdef NDEBUG
+#define g_value_get_boolean(v) (((const GValue *) (v))->data[0].v_int)
+#define g_value_get_char(v) (((const GValue *) (v))->data[0].v_int)
+#define g_value_get_uchar(v) (((const GValue *) (v))->data[0].v_uint)
+#define g_value_get_int(v) (((const GValue *) (v))->data[0].v_int)
+#define g_value_get_uint(v) (((const GValue *) (v))->data[0].v_uint)
+#define g_value_get_long(v) (((const GValue *) (v))->data[0].v_long)
+#define g_value_get_ulong(v) (((const GValue *) (v))->data[0].v_ulong)
+#define g_value_get_int64(v) (((const GValue *) (v))->data[0].v_int64)
+#define g_value_get_uint64(v) (((const GValue *) (v))->data[0].v_uint64)
+#define g_value_get_enum(v) (((const GValue *) (v))->data[0].v_long)
+#define g_value_get_flags(v) (((const GValue *) (v))->data[0].v_ulong)
+#define g_value_get_float(v) (((const GValue *) (v))->data[0].v_float)
+#define g_value_get_double(v) (((const GValue *) (v))->data[0].v_double)
+#define g_value_get_string(v) (((const GValue *) (v))->data[0].v_pointer)
+#define g_value_get_param(v) (((const GValue *) (v))->data[0].v_pointer)
+#define g_value_get_boxed(v) (((const GValue *) (v))->data[0].v_pointer)
+#define g_value_get_pointer(v) (((const GValue *) (v))->data[0].v_pointer)
+#define g_value_get_object(v) (((const GValue *) (v))->data[0].v_pointer)
+#endif
+
+/* properly set guess branch probability for pure booleans */
+#undef G_LIKELY
+#undef G_UNLIKELY
+
+#if defined(NDEBUG) && defined(__GNUC__) && (__GNUC__ > 2)
+#define G_LIKELY(expr) (__builtin_expect (!!(expr), TRUE))
+#define G_UNLIKELY(expr) (__builtin_expect (!!(expr), FALSE))
+#else
+#define G_LIKELY(expr) (expr)
+#define G_UNLIKELY(expr) (expr)
+#endif
+
+/* tooltip api */
+#if GTK_CHECK_VERSION (2,12,0)
+#define mousepad_widget_set_tooltip_text(widget,text) (gtk_widget_set_tooltip_text (widget, text))
+#else
+#define mousepad_widget_set_tooltip_text(widget,text) (mousepad_util_set_tooltip (widget, text))
+#endif
+
+G_END_DECLS
+
+#endif /* !__MOUSEPAD_PRIVATE_H__ */
diff --git a/mousepad/mousepad-replace-dialog.c b/mousepad/mousepad-replace-dialog.c
new file mode 100644
index 0000000..a3a171e
--- /dev/null
+++ b/mousepad/mousepad-replace-dialog.c
@@ -0,0 +1,653 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <mousepad/mousepad-private.h>
+#include <mousepad/mousepad-document.h>
+#include <mousepad/mousepad-replace-dialog.h>
+#include <mousepad/mousepad-dialogs.h>
+#include <mousepad/mousepad-preferences.h>
+#include <mousepad/mousepad-util.h>
+#include <mousepad/mousepad-marshal.h>
+
+
+
+static void mousepad_replace_dialog_unrealize (GtkWidget *widget);
+static void mousepad_replace_dialog_finalize (GObject *object);
+static void mousepad_replace_dialog_response (GtkWidget *widget,
+ gint response_id);
+static void mousepad_replace_dialog_changed (MousepadReplaceDialog *dialog);
+static void mousepad_replace_dialog_case_sensitive_toggled (GtkToggleButton *button,
+ MousepadReplaceDialog *dialog);
+static void mousepad_replace_dialog_whole_word_toggled (GtkToggleButton *button,
+ MousepadReplaceDialog *dialog);
+static void mousepad_replace_dialog_replace_all_toggled (GtkToggleButton *button,
+ MousepadReplaceDialog *dialog);
+static void mousepad_replace_dialog_search_location_changed (GtkComboBox *combo,
+ MousepadReplaceDialog *dialog);
+static void mousepad_replace_dialog_direction_changed (GtkComboBox *combo,
+ MousepadReplaceDialog *dialog);
+static void mousepad_replace_dialog_history_combo_box (GtkComboBox *combo_box);
+static void mousepad_replace_dialog_history_insert_text (const gchar *text);
+
+
+
+struct _MousepadReplaceDialogClass
+{
+ GtkDialogClass __parent__;
+};
+
+struct _MousepadReplaceDialog
+{
+ GtkDialog __parent__;
+
+ /* mousepad preferences */
+ MousepadPreferences *preferences;
+
+ /* dialog widgets */
+ GtkWidget *search_entry;
+ GtkWidget *replace_entry;
+ GtkWidget *find_button;
+ GtkWidget *replace_button;
+ GtkWidget *search_location_combo;
+ GtkWidget *hits_label;
+
+ /* search flags */
+ guint search_direction;
+ guint match_case : 1;
+ guint match_whole_word : 1;
+ guint replace_all : 1;
+ guint replace_all_location;
+};
+
+enum
+{
+ IN_SELECTION = 0,
+ IN_DOCUMENT,
+ IN_ALL_DOCUMENTS
+};
+
+enum
+{
+ DIRECTION_UP = 0,
+ DIRECTION_DOWN,
+ DIRECTION_BOTH
+};
+
+enum
+{
+ SEARCH,
+ LAST_SIGNAL
+};
+
+
+
+static GSList *history_list = NULL;
+static guint dialog_signals[LAST_SIGNAL];
+
+
+
+G_DEFINE_TYPE (MousepadReplaceDialog, mousepad_replace_dialog, GTK_TYPE_DIALOG);
+
+
+static void
+mousepad_replace_dialog_class_init (MousepadReplaceDialogClass *klass)
+{
+ GObjectClass *gobject_class;
+ GtkWidgetClass *gtkwidget_class;
+
+ gobject_class = G_OBJECT_CLASS (klass);
+ gobject_class->finalize = mousepad_replace_dialog_finalize;
+
+ gtkwidget_class = GTK_WIDGET_CLASS (klass);
+ gtkwidget_class->unrealize = mousepad_replace_dialog_unrealize;
+
+ dialog_signals[SEARCH] =
+ g_signal_new (I_("search"),
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL,
+ _mousepad_marshal_INT__FLAGS_STRING_STRING,
+ G_TYPE_INT, 3,
+ MOUSEPAD_TYPE_SEARCH_FLAGS,
+ G_TYPE_STRING, G_TYPE_STRING);
+}
+
+
+
+static void
+mousepad_replace_dialog_init (MousepadReplaceDialog *dialog)
+{
+ GtkWidget *vbox, *hbox, *combo, *label, *check;
+ GtkSizeGroup *size_group;
+ gboolean match_whole_word, match_case;
+ gint search_direction, replace_all_location;
+
+ /* initialize some variables */
+ dialog->replace_all = FALSE;
+
+ /* get the mousepad preferences */
+ dialog->preferences = mousepad_preferences_get ();
+
+ /* read the preferences */
+ g_object_get (G_OBJECT (dialog->preferences),
+ "search-match-whole-word", &match_whole_word,
+ "search-match-case", &match_case,
+ "search-direction", &search_direction,
+ "search-replace-all-location", &replace_all_location,
+ NULL);
+
+ /* set some flags */
+ dialog->match_whole_word = match_whole_word;
+ dialog->match_case = match_case;
+ dialog->search_direction = search_direction;
+ dialog->replace_all_location = replace_all_location;
+
+ /* set dialog properties */
+ gtk_window_set_title (GTK_WINDOW (dialog), _("Replace"));
+ gtk_window_set_default_size (GTK_WINDOW (dialog), 400, -1);
+ gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
+ g_signal_connect (G_OBJECT (dialog), "response", G_CALLBACK (mousepad_replace_dialog_response), NULL);
+
+ /* dialog buttons */
+ dialog->find_button = gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_FIND, MOUSEPAD_RESPONSE_FIND);
+ dialog->replace_button = mousepad_util_image_button (GTK_STOCK_FIND_AND_REPLACE, _("_Replace"));
+ gtk_dialog_add_action_widget (GTK_DIALOG (dialog), dialog->replace_button, MOUSEPAD_RESPONSE_REPLACE);
+ gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_CLOSE, MOUSEPAD_RESPONSE_CLOSE);
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), MOUSEPAD_RESPONSE_FIND);
+
+ /* create main vertical box */
+ vbox = g_object_new (GTK_TYPE_VBOX, "border-width", 6, "spacing", 4, NULL);
+ gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), vbox, TRUE, TRUE, 0);
+ gtk_widget_show (vbox);
+
+ /* horizontal box for search string */
+ hbox = gtk_hbox_new (FALSE, 8);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
+ gtk_widget_show (hbox);
+
+ /* create a size group */
+ size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+
+ label = gtk_label_new_with_mnemonic (_("_Search for:"));
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+ gtk_size_group_add_widget (size_group, label);
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+ gtk_widget_show (label);
+
+ combo = gtk_combo_box_entry_new_text ();
+ mousepad_replace_dialog_history_combo_box (GTK_COMBO_BOX (combo));
+ gtk_box_pack_start (GTK_BOX (hbox), combo, TRUE, TRUE, 0);
+ gtk_label_set_mnemonic_widget (GTK_LABEL(label), combo);
+ gtk_widget_show (combo);
+
+ /* store as an entry widget */
+ dialog->search_entry = gtk_bin_get_child (GTK_BIN (combo));
+ g_signal_connect_swapped (G_OBJECT (dialog->search_entry), "changed", G_CALLBACK (mousepad_replace_dialog_changed), dialog);
+
+ /* horizontal box for replace string */
+ hbox = gtk_hbox_new (FALSE, 8);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
+ gtk_widget_show (hbox);
+
+ label = gtk_label_new_with_mnemonic (_("Replace _with:"));
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+ gtk_size_group_add_widget (size_group, label);
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+ gtk_widget_show (label);
+
+ combo = gtk_combo_box_entry_new_text ();
+ mousepad_replace_dialog_history_combo_box (GTK_COMBO_BOX (combo));
+ gtk_box_pack_start (GTK_BOX (hbox), combo, TRUE, TRUE, 0);
+ gtk_widget_show (combo);
+
+ /* store as an entry widget */
+ dialog->replace_entry = gtk_bin_get_child (GTK_BIN (combo));
+
+ /* search direction */
+ hbox = gtk_hbox_new (FALSE, 8);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
+ gtk_label_set_mnemonic_widget (GTK_LABEL(label), combo);
+ gtk_widget_show (hbox);
+
+ label = gtk_label_new_with_mnemonic (_("Search _direction:"));
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+ gtk_size_group_add_widget (size_group, label);
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+ gtk_widget_show (label);
+
+ combo = gtk_combo_box_new_text ();
+ gtk_box_pack_start (GTK_BOX (hbox), combo, FALSE, FALSE, 0);
+ gtk_label_set_mnemonic_widget (GTK_LABEL(label), combo);
+ gtk_combo_box_append_text (GTK_COMBO_BOX (combo), _("Up"));
+ gtk_combo_box_append_text (GTK_COMBO_BOX (combo), _("Down"));
+ gtk_combo_box_append_text (GTK_COMBO_BOX (combo), _("Both"));
+ gtk_combo_box_set_active (GTK_COMBO_BOX (combo), search_direction);
+ g_signal_connect (G_OBJECT (combo), "changed", G_CALLBACK (mousepad_replace_dialog_direction_changed), dialog);
+ gtk_widget_show (combo);
+
+ /* release size group */
+ g_object_unref (G_OBJECT (size_group));
+
+ /* case sensitive */
+ check = gtk_check_button_new_with_mnemonic (_("Case sensi_tive"));
+ gtk_box_pack_start (GTK_BOX (vbox), check, FALSE, FALSE, 0);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), match_case);
+ g_signal_connect (G_OBJECT (check), "toggled", G_CALLBACK (mousepad_replace_dialog_case_sensitive_toggled), dialog);
+ gtk_widget_show (check);
+
+ /* match whole word */
+ check = gtk_check_button_new_with_mnemonic (_("_Match whole word"));
+ gtk_box_pack_start (GTK_BOX (vbox), check, FALSE, FALSE, 0);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), match_whole_word);
+ g_signal_connect (G_OBJECT (check), "toggled", G_CALLBACK (mousepad_replace_dialog_whole_word_toggled), dialog);
+ gtk_widget_show (check);
+
+ /* horizontal box for the replace all options */
+ hbox = gtk_hbox_new (FALSE, 8);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ check = gtk_check_button_new_with_mnemonic (_("Replace _all in:"));
+ gtk_box_pack_start (GTK_BOX (hbox), check, FALSE, FALSE, 0);
+ g_signal_connect (G_OBJECT (check), "toggled", G_CALLBACK (mousepad_replace_dialog_replace_all_toggled), dialog);
+ gtk_widget_show (check);
+
+ combo = dialog->search_location_combo = gtk_combo_box_new_text ();
+ gtk_box_pack_start (GTK_BOX (hbox), combo, FALSE, FALSE, 0);
+ gtk_combo_box_append_text (GTK_COMBO_BOX (combo), _("Selection"));
+ gtk_combo_box_append_text (GTK_COMBO_BOX (combo), _("Document"));
+ gtk_combo_box_append_text (GTK_COMBO_BOX (combo), _("All Documents"));
+ gtk_combo_box_set_active (GTK_COMBO_BOX (combo), replace_all_location);
+ gtk_widget_set_sensitive (combo, FALSE);
+ g_signal_connect (G_OBJECT (combo), "changed", G_CALLBACK (mousepad_replace_dialog_search_location_changed), dialog);
+ gtk_widget_show (combo);
+
+ label = dialog->hits_label = gtk_label_new (NULL);
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+}
+
+
+
+static void
+mousepad_replace_dialog_unrealize (GtkWidget *widget)
+{
+ MousepadReplaceDialog *dialog = MOUSEPAD_REPLACE_DIALOG (widget);
+ const gchar *text;
+
+ mousepad_return_if_fail (GTK_IS_ENTRY (dialog->replace_entry));
+ mousepad_return_if_fail (GTK_IS_ENTRY (dialog->search_entry));
+
+ text = gtk_entry_get_text (GTK_ENTRY (dialog->search_entry));
+ mousepad_replace_dialog_history_insert_text (text);
+
+ text = gtk_entry_get_text (GTK_ENTRY (dialog->replace_entry));
+ mousepad_replace_dialog_history_insert_text (text);
+
+ (*GTK_WIDGET_CLASS (mousepad_replace_dialog_parent_class)->unrealize) (widget);
+}
+
+
+
+static void
+mousepad_replace_dialog_finalize (GObject *object)
+{
+ MousepadReplaceDialog *dialog = MOUSEPAD_REPLACE_DIALOG (object);
+
+ /* release the preferences */
+ g_object_unref (G_OBJECT (dialog->preferences));
+
+ (*G_OBJECT_CLASS (mousepad_replace_dialog_parent_class)->finalize) (object);
+}
+
+
+
+static void
+mousepad_replace_dialog_response (GtkWidget *widget,
+ gint response_id)
+{
+ MousepadSearchFlags flags;
+ MousepadReplaceDialog *dialog = MOUSEPAD_REPLACE_DIALOG (widget);
+ gint matches;
+ const gchar *search_str, *replace_str;
+ gchar *message;
+
+ /* close dialog */
+ if (response_id == MOUSEPAD_RESPONSE_CLOSE)
+ goto destroy_dialog;
+
+ /* search direction */
+ if (dialog->search_direction == DIRECTION_UP
+ && dialog->replace_all == FALSE)
+ flags = MOUSEPAD_SEARCH_FLAGS_DIR_BACKWARD;
+ else
+ flags = MOUSEPAD_SEARCH_FLAGS_DIR_FORWARD;
+
+ /* case sensitive searching */
+ if (dialog->match_case)
+ flags |= MOUSEPAD_SEARCH_FLAGS_MATCH_CASE;
+
+ /* only match whole words */
+ if (dialog->match_whole_word)
+ flags |= MOUSEPAD_SEARCH_FLAGS_WHOLE_WORD;
+
+ /* wrap around */
+ if (dialog->search_direction == DIRECTION_BOTH
+ && dialog->replace_all == FALSE)
+ flags |= MOUSEPAD_SEARCH_FLAGS_WRAP_AROUND;
+
+ /* search area */
+ if (dialog->replace_all
+ && dialog->replace_all_location == IN_SELECTION)
+ flags |= MOUSEPAD_SEARCH_FLAGS_AREA_SELECTION;
+ else
+ flags |= MOUSEPAD_SEARCH_FLAGS_AREA_DOCUMENT;
+
+ /* start position */
+ if (response_id == MOUSEPAD_RESPONSE_CHECK_ENTRY)
+ {
+ /* no visible actions */
+ flags |= MOUSEPAD_SEARCH_FLAGS_ACTION_NONE;
+
+ if (dialog->replace_all)
+ goto replace_flags;
+ else
+ goto search_flags;
+ }
+ else if (response_id == MOUSEPAD_RESPONSE_FIND)
+ {
+ /* select the first match */
+ flags |= MOUSEPAD_SEARCH_FLAGS_ACTION_SELECT;
+
+ search_flags:
+
+ /* start at the 'end' of the selection */
+ if (flags & MOUSEPAD_SEARCH_FLAGS_DIR_BACKWARD)
+ flags |= MOUSEPAD_SEARCH_FLAGS_ITER_SEL_START;
+ else
+ flags |= MOUSEPAD_SEARCH_FLAGS_ITER_SEL_END;
+ }
+ else if (response_id == MOUSEPAD_RESPONSE_REPLACE)
+ {
+ /* replace matches */
+ flags |= MOUSEPAD_SEARCH_FLAGS_ACTION_REPLACE;
+
+ if (dialog->replace_all)
+ {
+ replace_flags:
+
+ /* replace all from the beginning of the document */
+ flags |= MOUSEPAD_SEARCH_FLAGS_ITER_AREA_START
+ | MOUSEPAD_SEARCH_FLAGS_ENTIRE_AREA;
+
+ /* search all opened documents (flag used in mousepad-window.c) */
+ if (dialog->replace_all_location == IN_ALL_DOCUMENTS)
+ flags |= MOUSEPAD_SEARCH_FLAGS_ALL_DOCUMENTS;
+ }
+ else
+ {
+ /* start at the 'beginning' of the selection */
+ if (flags & MOUSEPAD_SEARCH_FLAGS_DIR_BACKWARD)
+ flags |= MOUSEPAD_SEARCH_FLAGS_ITER_SEL_END;
+ else
+ flags |= MOUSEPAD_SEARCH_FLAGS_ITER_SEL_START;
+ }
+ }
+ else
+ {
+ destroy_dialog:
+
+ /* destroy the window */
+ gtk_widget_destroy (widget);
+
+ /* leave */
+ return;
+ }
+
+ /* get strings */
+ search_str = gtk_entry_get_text (GTK_ENTRY (dialog->search_entry));
+ replace_str = gtk_entry_get_text (GTK_ENTRY (dialog->replace_entry));
+
+ /* emit the signal */
+ g_signal_emit (G_OBJECT (dialog), dialog_signals[SEARCH], 0, flags, search_str, replace_str, &matches);
+
+ /* reset counter */
+ if (response_id == MOUSEPAD_RESPONSE_REPLACE && dialog->replace_all)
+ matches = 0;
+
+ /* update entry color */
+ mousepad_util_entry_error (dialog->search_entry, matches == 0);
+
+ /* update counter */
+ if (dialog->replace_all)
+ {
+ message = g_strdup_printf (ngettext ("%d occurence", "%d occurences", matches), matches);
+ gtk_label_set_markup (GTK_LABEL (dialog->hits_label), message);
+ g_free (message);
+ }
+}
+
+
+static void
+mousepad_replace_dialog_changed (MousepadReplaceDialog *dialog)
+{
+ const gchar *text;
+ gboolean sensitive;
+
+ /* get the search entry text */
+ text = gtk_entry_get_text (GTK_ENTRY (dialog->search_entry));
+
+ if (text != NULL && *text != '\0')
+ {
+ /* do an invisible search to give the user some visible feedback */
+ gtk_dialog_response (GTK_DIALOG (dialog), MOUSEPAD_RESPONSE_CHECK_ENTRY);
+
+ /* buttons are sensitive */
+ sensitive = TRUE;
+ }
+ else
+ {
+ /* not text, means no error */
+ mousepad_util_entry_error (dialog->search_entry, FALSE);
+
+ /* reset occurences label */
+ gtk_label_set_text (GTK_LABEL (dialog->hits_label), NULL);
+
+ /* buttons are not sensitive */
+ sensitive = FALSE;
+ }
+
+ /* set the sensitivity */
+ gtk_widget_set_sensitive (dialog->find_button, sensitive);
+ gtk_widget_set_sensitive (dialog->replace_button, sensitive);
+}
+
+
+
+static void
+mousepad_replace_dialog_case_sensitive_toggled (GtkToggleButton *button,
+ MousepadReplaceDialog *dialog)
+{
+ /* get the toggle button state */
+ dialog->match_case = gtk_toggle_button_get_active (button);
+
+ /* save the setting */
+ g_object_set (G_OBJECT (dialog->preferences), "search-match-case", dialog->match_case, NULL);
+
+ /* update dialog */
+ mousepad_replace_dialog_changed (dialog);
+}
+
+
+
+static void
+mousepad_replace_dialog_whole_word_toggled (GtkToggleButton *button,
+ MousepadReplaceDialog *dialog)
+{
+ /* get the toggle button state */
+ dialog->match_whole_word = gtk_toggle_button_get_active (button);
+
+ /* save the setting */
+ g_object_set (G_OBJECT (dialog->preferences), "search-match-whole-word", dialog->match_whole_word, NULL);
+
+ /* update dialog */
+ mousepad_replace_dialog_changed (dialog);
+}
+
+
+
+static void
+mousepad_replace_dialog_replace_all_toggled (GtkToggleButton *button,
+ MousepadReplaceDialog *dialog)
+{
+ gboolean active;
+
+ /* get the toggle button state */
+ active = gtk_toggle_button_get_active (button);
+
+ /* save flags */
+ dialog->replace_all = active;
+
+ /* set the sensitivity of some dialog widgets */
+ gtk_widget_set_sensitive (dialog->search_location_combo, active);
+
+ /* reset occurences label */
+ gtk_label_set_text (GTK_LABEL (dialog->hits_label), NULL);
+
+ /* set new label of the replace button */
+ gtk_button_set_label (GTK_BUTTON (dialog->replace_button),
+ active ? _("_Replace All") : _("_Replace"));
+
+ /* update dialog */
+ mousepad_replace_dialog_changed (dialog);
+}
+
+
+
+static void
+mousepad_replace_dialog_search_location_changed (GtkComboBox *combo,
+ MousepadReplaceDialog *dialog)
+{
+ /* get the selected action */
+ dialog->replace_all_location = gtk_combo_box_get_active (combo);
+
+ /* save setting */
+ g_object_set (G_OBJECT (dialog->preferences), "search-replace-all-location", dialog->replace_all_location, NULL);
+
+ /* update dialog */
+ mousepad_replace_dialog_changed (dialog);
+}
+
+
+
+static void
+mousepad_replace_dialog_direction_changed (GtkComboBox *combo,
+ MousepadReplaceDialog *dialog)
+{
+ /* get the selected item */
+ dialog->search_direction = gtk_combo_box_get_active (combo);
+
+ /* save the last direction */
+ g_object_set (G_OBJECT (dialog->preferences), "search-direction", dialog->search_direction, NULL);
+
+ /* update dialog */
+ mousepad_replace_dialog_changed (dialog);
+}
+
+
+
+/**
+ * History functions
+ **/
+static void
+mousepad_replace_dialog_history_combo_box (GtkComboBox *combo_box)
+{
+ GSList *li;
+
+ mousepad_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
+
+ /* append the items from the history to the combobox */
+ for (li = history_list; li != NULL; li = li->next)
+ gtk_combo_box_append_text (combo_box, li->data);
+}
+
+
+
+static void
+mousepad_replace_dialog_history_insert_text (const gchar *text)
+{
+ GSList *li;
+
+ /* quit if the box is empty */
+ if (text == NULL || *text == '\0')
+ return;
+
+ /* check if the string is already in the history */
+ for (li = history_list; li != NULL; li = li->next)
+ if (strcmp (li->data, text) == 0)
+ return;
+
+ /* prepend the string */
+ history_list = g_slist_prepend (history_list, g_strdup (text));
+}
+
+
+
+GtkWidget *
+mousepad_replace_dialog_new (void)
+{
+ return g_object_new (MOUSEPAD_TYPE_REPLACE_DIALOG, NULL);
+}
+
+
+
+void
+mousepad_replace_dialog_history_clean (void)
+{
+ GSList *li;
+
+ if (history_list)
+ {
+ /* remove all the entries */
+ for (li = history_list; li != NULL; li = li->next)
+ {
+ /* cleanup the string */
+ g_free (li->data);
+
+ /* remove the item from the list */
+ history_list = g_slist_delete_link (history_list, li);
+ }
+
+ /* cleanup the list */
+ g_slist_free (history_list);
+ }
+}
+
+
+
+void
+mousepad_replace_dialog_page_switched (MousepadReplaceDialog *dialog)
+{
+ mousepad_replace_dialog_changed (dialog);
+}
diff --git a/mousepad/mousepad-replace-dialog.h b/mousepad/mousepad-replace-dialog.h
new file mode 100644
index 0000000..b3e7d2f
--- /dev/null
+++ b/mousepad/mousepad-replace-dialog.h
@@ -0,0 +1,42 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __MOUSEPAD_REPLACE_DIALOG_H__
+#define __MOUSEPAD_REPLACE_DIALOG_H__
+
+G_BEGIN_DECLS
+
+#define MOUSEPAD_TYPE_REPLACE_DIALOG (mousepad_replace_dialog_get_type ())
+#define MOUSEPAD_REPLACE_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MOUSEPAD_TYPE_REPLACE_DIALOG, MousepadReplaceDialog))
+#define MOUSEPAD_REPLACE_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MOUSEPAD_TYPE_REPLACE_DIALOG, MousepadReplaceDialogClass))
+#define MOUSEPAD_IS_REPLACE_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MOUSEPAD_TYPE_REPLACE_DIALOG))
+#define MOUSEPAD_IS_REPLACE_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MOUSEPADL_TYPE_REPLACE_DIALOG))
+#define MOUSEPAD_REPLACE_DIALOG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MOUSEPAD_TYPE_REPLACE_DIALOG, MousepadReplaceDialogClass))
+
+typedef struct _MousepadReplaceDialogClass MousepadReplaceDialogClass;
+typedef struct _MousepadReplaceDialog MousepadReplaceDialog;
+
+GType mousepad_replace_dialog_get_type (void) G_GNUC_CONST;
+
+GtkWidget *mousepad_replace_dialog_new (void);
+
+void mousepad_replace_dialog_history_clean (void);
+
+void mousepad_replace_dialog_page_switched (MousepadReplaceDialog *dialog);
+
+G_END_DECLS
+
+#endif /* !__MOUSEPAD_REPLACE_DIALOG_H__ */
diff --git a/mousepad/mousepad-search-bar.c b/mousepad/mousepad-search-bar.c
new file mode 100644
index 0000000..4158d02
--- /dev/null
+++ b/mousepad/mousepad-search-bar.c
@@ -0,0 +1,536 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gdk/gdk.h>
+#include <gdk/gdkkeysyms.h>
+
+#include <mousepad/mousepad-private.h>
+#include <mousepad/mousepad-marshal.h>
+#include <mousepad/mousepad-document.h>
+#include <mousepad/mousepad-search-bar.h>
+#include <mousepad/mousepad-preferences.h>
+#include <mousepad/mousepad-util.h>
+#include <mousepad/mousepad-window.h>
+
+
+
+#define TOOL_BAR_ICON_SIZE GTK_ICON_SIZE_MENU
+#define HIGHTLIGHT_TIMEOUT 225
+
+
+
+static void mousepad_search_bar_finalize (GObject *object);
+static void mousepad_search_bar_find_string (MousepadSearchBar *bar,
+ MousepadSearchFlags flags);
+static void mousepad_search_bar_hide_clicked (MousepadSearchBar *bar);
+static void mousepad_search_bar_entry_changed (GtkWidget *entry,
+ MousepadSearchBar *bar);
+static void mousepad_search_bar_highlight_toggled (GtkWidget *button,
+ MousepadSearchBar *bar);
+static void mousepad_search_bar_match_case_toggled (GtkWidget *button,
+ MousepadSearchBar *bar);
+static void mousepad_search_bar_menuitem_toggled (GtkCheckMenuItem *item,
+ GtkToggleButton *button);
+static void mousepad_search_bar_highlight_schedule (MousepadSearchBar *bar);
+static gboolean mousepad_search_bar_highlight_timeout (gpointer user_data);
+static void mousepad_search_bar_highlight_timeout_destroy (gpointer user_data);
+
+
+
+enum
+{
+ HIDE_BAR,
+ SEARCH,
+ LAST_SIGNAL
+};
+
+struct _MousepadSearchBarClass
+{
+ GtkToolbarClass __parent__;
+};
+
+struct _MousepadSearchBar
+{
+ GtkToolbar __parent__;
+
+ /* preferences */
+ MousepadPreferences *preferences;
+
+ /* text entry */
+ GtkWidget *entry;
+
+ /* menu entries */
+ GtkWidget *match_case_entry;
+
+ /* flags */
+ guint highlight_all : 1;
+ guint match_case : 1;
+
+ /* highlight id */
+ guint highlight_id;
+};
+
+
+
+static guint search_bar_signals[LAST_SIGNAL];
+
+
+
+GtkWidget *
+mousepad_search_bar_new (void)
+{
+ return g_object_new (MOUSEPAD_TYPE_SEARCH_BAR,
+ "toolbar-style", GTK_TOOLBAR_BOTH_HORIZ,
+ "icon-size", TOOL_BAR_ICON_SIZE,
+ NULL);
+}
+
+
+
+G_DEFINE_TYPE (MousepadSearchBar, mousepad_search_bar, GTK_TYPE_TOOLBAR);
+
+
+
+static void
+mousepad_search_bar_class_init (MousepadSearchBarClass *klass)
+{
+ GObjectClass *gobject_class;
+ GtkBindingSet *binding_set;
+
+ gobject_class = G_OBJECT_CLASS (klass);
+ gobject_class->finalize = mousepad_search_bar_finalize;
+
+ /* signals */
+ search_bar_signals[HIDE_BAR] =
+ g_signal_new (I_("hide-bar"),
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ 0, NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ search_bar_signals[SEARCH] =
+ g_signal_new (I_("search"),
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL,
+ _mousepad_marshal_INT__FLAGS_STRING_STRING,
+ G_TYPE_INT, 3,
+ MOUSEPAD_TYPE_SEARCH_FLAGS,
+ G_TYPE_STRING, G_TYPE_STRING);
+
+ /* setup key bindings for the search bar */
+ binding_set = gtk_binding_set_by_class (klass);
+ gtk_binding_entry_add_signal (binding_set, GDK_Escape, 0, "hide-bar", 0);
+
+ /* hide the shadow around the toolbar */
+ gtk_rc_parse_string ("style \"mousepad-search-bar-style\"\n"
+ "{\n"
+ "GtkToolbar::shadow-type = GTK_SHADOW_NONE\n"
+ "}\n"
+ "class \"MousepadSearchBar\" style \"mousepad-search-bar-style\"\n"
+
+ /* add 2px space between the toolbar buttons */
+ "style \"mousepad-button-style\"\n"
+ "{\n"
+ "GtkToolButton::icon-spacing = 2\n"
+ "}\n"
+ "widget \"MousepadWindow.*.Gtk*ToolButton\" style \"mousepad-button-style\"\n");
+}
+
+
+
+static void
+mousepad_search_bar_init (MousepadSearchBar *bar)
+{
+ GtkWidget *label, *image, *check, *menuitem;
+ GtkToolItem *item;
+ gboolean match_case;
+
+ /* preferences */
+ bar->preferences = mousepad_preferences_get ();
+
+ /* load some preferences */
+ g_object_get (G_OBJECT (bar->preferences), "search-match-case", &match_case, NULL);
+
+ /* init variables */
+ bar->highlight_id = 0;
+ bar->match_case = match_case;
+
+ /* the close button */
+ item = gtk_tool_button_new_from_stock (GTK_STOCK_CLOSE);
+ gtk_toolbar_insert (GTK_TOOLBAR (bar), item, -1);
+ g_signal_connect_swapped (G_OBJECT (item), "clicked", G_CALLBACK (mousepad_search_bar_hide_clicked), bar);
+ gtk_widget_show (GTK_WIDGET (item));
+
+ /* the find label */
+ item = gtk_tool_item_new ();
+ gtk_toolbar_insert (GTK_TOOLBAR (bar), item, -1);
+ gtk_widget_show (GTK_WIDGET (item));
+
+ label = gtk_label_new_with_mnemonic (_("Fi_nd:"));
+ gtk_container_add (GTK_CONTAINER (item), label);
+ gtk_misc_set_padding (GTK_MISC (label), 2, 0);
+ gtk_widget_show (label);
+
+ /* the entry field */
+ item = gtk_tool_item_new ();
+ gtk_toolbar_insert (GTK_TOOLBAR (bar), item, -1);
+ gtk_widget_show (GTK_WIDGET (item));
+
+ bar->entry = gtk_entry_new ();
+ gtk_container_add (GTK_CONTAINER (item), bar->entry);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), bar->entry);
+ g_signal_connect (G_OBJECT (bar->entry), "changed", G_CALLBACK (mousepad_search_bar_entry_changed), bar);
+ gtk_widget_show (bar->entry);
+
+ /* next button */
+ image = gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, TOOL_BAR_ICON_SIZE);
+ gtk_widget_show (image);
+
+ item = gtk_tool_button_new (image, _("_Next"));
+ gtk_tool_item_set_is_important (item, TRUE);
+ gtk_tool_button_set_use_underline (GTK_TOOL_BUTTON (item), TRUE);
+ gtk_toolbar_insert (GTK_TOOLBAR (bar), item, -1);
+ g_signal_connect_swapped (G_OBJECT (item), "clicked", G_CALLBACK (mousepad_search_bar_find_next), bar);
+ gtk_widget_show (GTK_WIDGET (item));
+
+ /* previous button */
+ image = gtk_image_new_from_stock (GTK_STOCK_GO_UP, TOOL_BAR_ICON_SIZE);
+ gtk_widget_show (image);
+
+ item = gtk_tool_button_new (image, _("_Previous"));
+ gtk_tool_item_set_is_important (item, TRUE);
+ gtk_tool_button_set_use_underline (GTK_TOOL_BUTTON (item), TRUE);
+ gtk_toolbar_insert (GTK_TOOLBAR (bar), item, -1);
+ g_signal_connect_swapped (G_OBJECT (item), "clicked", G_CALLBACK (mousepad_search_bar_find_previous), bar);
+ gtk_widget_show (GTK_WIDGET (item));
+
+ /* highlight all */
+ item = (GtkToolItem *) gtk_toggle_tool_button_new ();
+ g_signal_connect_object (G_OBJECT (bar), "destroy", G_CALLBACK (gtk_widget_destroy), item, G_CONNECT_SWAPPED);
+ gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (item), GTK_STOCK_SELECT_ALL);
+ gtk_tool_button_set_label (GTK_TOOL_BUTTON (item), _("Highlight _All"));
+ gtk_tool_item_set_is_important (item, TRUE);
+ gtk_tool_button_set_use_underline (GTK_TOOL_BUTTON (item), TRUE);
+ gtk_toolbar_insert (GTK_TOOLBAR (bar), item, -1);
+ g_signal_connect (G_OBJECT (item), "clicked", G_CALLBACK (mousepad_search_bar_highlight_toggled), bar);
+ gtk_widget_show (GTK_WIDGET (item));
+
+ /* check button for case sensitive, including the proxy menu item */
+ item = gtk_tool_item_new ();
+ g_signal_connect_object (G_OBJECT (bar), "destroy", G_CALLBACK (gtk_widget_destroy), item, G_CONNECT_SWAPPED);
+ gtk_toolbar_insert (GTK_TOOLBAR (bar), item, -1);
+ gtk_widget_show (GTK_WIDGET (item));
+
+ check = gtk_check_button_new_with_mnemonic (_("Mat_ch Case"));
+ g_signal_connect_object (G_OBJECT (bar), "destroy", G_CALLBACK (gtk_widget_destroy), item, G_CONNECT_SWAPPED);
+ gtk_container_add (GTK_CONTAINER (item), check);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), match_case);
+ g_signal_connect (G_OBJECT (check), "toggled", G_CALLBACK (mousepad_search_bar_match_case_toggled), bar);
+ gtk_widget_show (check);
+
+ bar->match_case_entry = menuitem = gtk_check_menu_item_new_with_mnemonic (_("Mat_ch Case"));
+ g_signal_connect_object (G_OBJECT (bar), "destroy", G_CALLBACK (gtk_widget_destroy), item, G_CONNECT_SWAPPED);
+ gtk_tool_item_set_proxy_menu_item (item, "case-sensitive", menuitem);
+ gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (menuitem), match_case);
+ g_signal_connect (G_OBJECT (menuitem), "toggled", G_CALLBACK (mousepad_search_bar_menuitem_toggled), check);
+ gtk_widget_show (menuitem);
+}
+
+
+
+static void
+mousepad_search_bar_finalize (GObject *object)
+{
+ MousepadSearchBar *bar = MOUSEPAD_SEARCH_BAR (object);
+
+ /* release the preferences */
+ g_object_unref (G_OBJECT (bar->preferences));
+
+ /* stop a running highlight timeout */
+ if (bar->highlight_id != 0)
+ g_source_remove (bar->highlight_id);
+
+ (*G_OBJECT_CLASS (mousepad_search_bar_parent_class)->finalize) (object);
+}
+
+
+
+static void
+mousepad_search_bar_find_string (MousepadSearchBar *bar,
+ MousepadSearchFlags flags)
+{
+ const gchar *string;
+ gint nmatches;
+
+ /* search the entire document */
+ flags |= MOUSEPAD_SEARCH_FLAGS_AREA_DOCUMENT;
+
+ /* if we don't hightlight, we select with wrapping */
+ if ((flags & MOUSEPAD_SEARCH_FLAGS_ACTION_HIGHTLIGHT) == 0)
+ flags |= MOUSEPAD_SEARCH_FLAGS_ACTION_SELECT
+ | MOUSEPAD_SEARCH_FLAGS_WRAP_AROUND;
+
+ /* append the insensitive flags when needed */
+ if (bar->match_case)
+ flags |= MOUSEPAD_SEARCH_FLAGS_MATCH_CASE;
+
+ /* get the entry string */
+ string = gtk_entry_get_text (GTK_ENTRY (bar->entry));
+
+ /* emit signal */
+ g_signal_emit (G_OBJECT (bar), search_bar_signals[SEARCH], 0, flags, string, NULL, &nmatches);
+
+ /* do nothing with the error entry when highlight when trigged with highlight */
+ if ((flags & MOUSEPAD_SEARCH_FLAGS_ACTION_HIGHTLIGHT) == 0)
+ {
+ /* make sure the search entry is not red when no text was typed */
+ if (string == NULL || *string == '\0')
+ nmatches = 1;
+
+ /* change the entry style */
+ mousepad_util_entry_error (bar->entry, nmatches < 1);
+ }
+}
+
+
+
+static void
+mousepad_search_bar_hide_clicked (MousepadSearchBar *bar)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_SEARCH_BAR (bar));
+
+ /* emit the signal */
+ g_signal_emit (G_OBJECT (bar), search_bar_signals[HIDE_BAR], 0);
+}
+
+
+
+static void
+mousepad_search_bar_entry_changed (GtkWidget *entry,
+ MousepadSearchBar *bar)
+{
+ MousepadSearchFlags flags;
+
+ /* set the search flags */
+ flags = MOUSEPAD_SEARCH_FLAGS_ITER_SEL_START
+ | MOUSEPAD_SEARCH_FLAGS_DIR_FORWARD;
+
+ /* find */
+ mousepad_search_bar_find_string (bar, flags);
+
+ /* schedule a new highlight */
+ mousepad_search_bar_highlight_schedule (bar);
+}
+
+
+
+static void
+mousepad_search_bar_highlight_toggled (GtkWidget *button,
+ MousepadSearchBar *bar)
+{
+ MousepadSearchFlags flags;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_SEARCH_BAR (bar));
+
+ /* set the new state */
+ bar->highlight_all = gtk_toggle_tool_button_get_active (GTK_TOGGLE_TOOL_BUTTON (button));
+
+ if (bar->highlight_all)
+ {
+ /* reschedule the highlight */
+ mousepad_search_bar_highlight_schedule (bar);
+ }
+ else
+ {
+ /* stop timeout */
+ if (bar->highlight_id != 0)
+ g_source_remove (bar->highlight_id);
+
+ /* set search flags */
+ flags = MOUSEPAD_SEARCH_FLAGS_ACTION_HIGHTLIGHT
+ | MOUSEPAD_SEARCH_FLAGS_ACTION_CLEANUP;
+
+ /* emit signal to cleanup the highlight */
+ mousepad_search_bar_find_string (bar, flags);
+ }
+}
+
+
+
+static void
+mousepad_search_bar_match_case_toggled (GtkWidget *button,
+ MousepadSearchBar *bar)
+{
+ gboolean active;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_SEARCH_BAR (bar));
+
+ /* get the state of the toggle button */
+ active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button));
+
+ /* set the state of the menu item */
+ gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (bar->match_case_entry), active);
+
+ /* save the state */
+ bar->match_case = active;
+
+ /* save the setting */
+ g_object_set (G_OBJECT (bar->preferences), "search-match-case", active, NULL);
+
+ /* search ahead with this new flags */
+ mousepad_search_bar_entry_changed (NULL, bar);
+
+ /* schedule a new hightlight */
+ mousepad_search_bar_highlight_schedule (bar);
+}
+
+
+
+static void
+mousepad_search_bar_menuitem_toggled (GtkCheckMenuItem *item,
+ GtkToggleButton *button)
+{
+ gboolean active;
+
+ mousepad_return_if_fail (GTK_IS_CHECK_MENU_ITEM (item));
+ mousepad_return_if_fail (GTK_IS_TOGGLE_BUTTON (button));
+
+ /* toggle the menubar item, he/she will send the signal */
+ active = gtk_check_menu_item_get_active (item);
+ gtk_toggle_button_set_active (button, active);
+}
+
+
+
+static void
+mousepad_search_bar_highlight_schedule (MousepadSearchBar *bar)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_SEARCH_BAR (bar));
+
+ /* stop a pending timeout */
+ if (bar->highlight_id != 0)
+ g_source_remove (bar->highlight_id);
+
+ /* schedule a new timeout */
+ if (bar->highlight_all)
+ {
+ bar->highlight_id = g_timeout_add_full (G_PRIORITY_LOW, HIGHTLIGHT_TIMEOUT, mousepad_search_bar_highlight_timeout,
+ bar, mousepad_search_bar_highlight_timeout_destroy);
+ }
+}
+
+
+
+static void
+mousepad_search_bar_highlight_timeout_destroy (gpointer user_data)
+{
+ MOUSEPAD_SEARCH_BAR (user_data)->highlight_id = 0;
+}
+
+
+
+static gboolean
+mousepad_search_bar_highlight_timeout (gpointer user_data)
+{
+ MousepadSearchBar *bar = MOUSEPAD_SEARCH_BAR (user_data);
+ MousepadSearchFlags flags;
+
+ GDK_THREADS_ENTER ();
+
+ /* set search flags */
+ flags = MOUSEPAD_SEARCH_FLAGS_DIR_FORWARD
+ | MOUSEPAD_SEARCH_FLAGS_ITER_AREA_START
+ | MOUSEPAD_SEARCH_FLAGS_ACTION_HIGHTLIGHT;
+
+ /* emit signal */
+ mousepad_search_bar_find_string (bar, flags);
+
+ GDK_THREADS_LEAVE ();
+
+ /* stop the timeout */
+ return FALSE;
+}
+
+
+
+GtkEditable *
+mousepad_search_bar_entry (MousepadSearchBar *bar)
+{
+ if (bar && GTK_WIDGET_HAS_FOCUS (bar->entry))
+ return GTK_EDITABLE (bar->entry);
+ else
+ return NULL;
+}
+
+
+
+void
+mousepad_search_bar_focus (MousepadSearchBar *bar)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_SEARCH_BAR (bar));
+
+ /* focus the entry field */
+ gtk_widget_grab_focus (bar->entry);
+
+ /* trigger search function */
+ mousepad_search_bar_entry_changed (NULL, bar);
+
+ /* update the highlight */
+ mousepad_search_bar_highlight_schedule (bar);
+
+ /* select the entire entry */
+ gtk_editable_select_region (GTK_EDITABLE (bar->entry), 0, -1);
+}
+
+
+
+void
+mousepad_search_bar_find_next (MousepadSearchBar *bar)
+{
+ MousepadSearchFlags flags;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_SEARCH_BAR (bar));
+
+ /* set search flags */
+ flags = MOUSEPAD_SEARCH_FLAGS_ITER_SEL_END
+ | MOUSEPAD_SEARCH_FLAGS_DIR_FORWARD;
+
+ /* search */
+ mousepad_search_bar_find_string (bar, flags);
+}
+
+
+
+void
+mousepad_search_bar_find_previous (MousepadSearchBar *bar)
+{
+ MousepadSearchFlags flags;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_SEARCH_BAR (bar));
+
+ /* set search flags */
+ flags = MOUSEPAD_SEARCH_FLAGS_ITER_SEL_START
+ | MOUSEPAD_SEARCH_FLAGS_DIR_BACKWARD;
+
+ /* search */
+ mousepad_search_bar_find_string (bar, flags);
+}
diff --git a/mousepad/mousepad-search-bar.h b/mousepad/mousepad-search-bar.h
new file mode 100644
index 0000000..4dbfe11
--- /dev/null
+++ b/mousepad/mousepad-search-bar.h
@@ -0,0 +1,46 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __MOUSEPAD_SEARCH_BAR_H__
+#define __MOUSEPAD_SEARCH_BAR_H__
+
+G_BEGIN_DECLS
+
+#define MOUSEPAD_TYPE_SEARCH_BAR (mousepad_search_bar_get_type ())
+#define MOUSEPAD_SEARCH_BAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MOUSEPAD_TYPE_SEARCH_BAR, MousepadSearchBar))
+#define MOUSEPAD_SEARCH_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MOUSEPAD_TYPE_SEARCH_BAR, MousepadSearchBarClass))
+#define MOUSEPAD_IS_SEARCH_BAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MOUSEPAD_TYPE_SEARCH_BAR))
+#define MOUSEPAD_IS_SEARCH_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MOUSEPADL_TYPE_SEARCH_BAR))
+#define MOUSEPAD_SEARCH_BAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MOUSEPAD_TYPE_SEARCH_BAR, MousepadSearchBarClass))
+
+typedef struct _MousepadSearchBarClass MousepadSearchBarClass;
+typedef struct _MousepadSearchBar MousepadSearchBar;
+
+GType mousepad_search_bar_get_type (void) G_GNUC_CONST;
+
+GtkWidget *mousepad_search_bar_new (void);
+
+GtkEditable *mousepad_search_bar_entry (MousepadSearchBar *bar);
+
+void mousepad_search_bar_focus (MousepadSearchBar *bar);
+
+void mousepad_search_bar_find_next (MousepadSearchBar *bar);
+
+void mousepad_search_bar_find_previous (MousepadSearchBar *bar);
+
+G_END_DECLS
+
+#endif /* !__MOUSEPAD_SEARCH_BAR_H__ */
diff --git a/mousepad/mousepad-statusbar.c b/mousepad/mousepad-statusbar.c
new file mode 100644
index 0000000..e69a494
--- /dev/null
+++ b/mousepad/mousepad-statusbar.c
@@ -0,0 +1,300 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <mousepad/mousepad-private.h>
+#include <mousepad/mousepad-statusbar.h>
+#include <mousepad/mousepad-window.h>
+#include <mousepad/mousepad-util.h>
+
+
+
+static gboolean mousepad_statusbar_overwrite_clicked (GtkWidget *widget,
+ GdkEventButton *event,
+ MousepadStatusbar *statusbar);
+
+static gboolean mousepad_statusbar_filetype_clicked (GtkWidget *widget,
+ GdkEventButton *event,
+ MousepadStatusbar *statusbar);
+
+
+
+enum
+{
+ ENABLE_OVERWRITE,
+ POPULATE_FILETYPE_POPUP,
+ LAST_SIGNAL,
+};
+
+struct _MousepadStatusbarClass
+{
+ GtkStatusbarClass __parent__;
+};
+
+struct _MousepadStatusbar
+{
+ GtkStatusbar __parent__;
+
+ /* whether overwrite is enabled */
+ guint overwrite_enabled : 1;
+
+ /* extra labels in the statusbar */
+ GtkWidget *language;
+ GtkWidget *position;
+ GtkWidget *overwrite;
+};
+
+
+
+static guint statusbar_signals[LAST_SIGNAL];
+
+
+
+G_DEFINE_TYPE (MousepadStatusbar, mousepad_statusbar, GTK_TYPE_STATUSBAR);
+
+
+
+GtkWidget *
+mousepad_statusbar_new (void)
+{
+ return g_object_new (MOUSEPAD_TYPE_STATUSBAR, NULL);
+}
+
+
+
+static void
+mousepad_statusbar_class_init (MousepadStatusbarClass *klass)
+{
+ GObjectClass *gobject_class;
+
+ gobject_class = G_OBJECT_CLASS (klass);
+
+ statusbar_signals[ENABLE_OVERWRITE] =
+ g_signal_new (I_("enable-overwrite"),
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL,
+ g_cclosure_marshal_VOID__BOOLEAN,
+ G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
+
+ statusbar_signals[POPULATE_FILETYPE_POPUP] =
+ g_signal_new (I_("populate-filetype-popup"),
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1, GTK_TYPE_MENU);
+}
+
+
+
+static void
+mousepad_statusbar_init (MousepadStatusbar *statusbar)
+{
+ GtkWidget *ebox, *box, *separator, *label;
+ GtkStatusbar *bar = GTK_STATUSBAR (statusbar);
+ GList *frame;
+
+ /* init statusbar */
+ gtk_statusbar_set_has_resize_grip (bar, TRUE);
+
+ /* create a new horizontal box */
+ box = gtk_hbox_new (FALSE, 8);
+ gtk_widget_show (box);
+
+ /* reorder the gtk statusbar */
+ frame = gtk_container_get_children (GTK_CONTAINER (bar));
+ gtk_frame_set_shadow_type (GTK_FRAME (frame->data), GTK_SHADOW_NONE);
+ label = gtk_bin_get_child (GTK_BIN (frame->data));
+ g_object_ref (label);
+ gtk_container_remove (GTK_CONTAINER (frame->data), label);
+ gtk_container_add (GTK_CONTAINER (frame->data), box);
+ gtk_box_pack_start (GTK_BOX (box), label, TRUE, TRUE, 0);
+ g_object_unref (label);
+ g_list_free (frame);
+
+ /* separator */
+ separator = gtk_vseparator_new ();
+ gtk_box_pack_start (GTK_BOX (box), separator, FALSE, FALSE, 0);
+ gtk_widget_show (separator);
+
+ /* language/filetype event box */
+ ebox = gtk_event_box_new ();
+ gtk_box_pack_start (GTK_BOX (box), ebox, FALSE, TRUE, 0);
+ gtk_event_box_set_visible_window (GTK_EVENT_BOX (ebox), FALSE);
+ mousepad_widget_set_tooltip_text (ebox, _("Choose a filetype"));
+ g_signal_connect (G_OBJECT (ebox), "button-press-event", G_CALLBACK (mousepad_statusbar_filetype_clicked), statusbar);
+ gtk_widget_show (ebox);
+
+ /* language/filetype */
+ statusbar->language = gtk_label_new (_("Filetype: None"));
+ gtk_container_add (GTK_CONTAINER (ebox), statusbar->language);
+ gtk_widget_show (statusbar->language);
+
+ /* separator */
+ separator = gtk_vseparator_new ();
+ gtk_box_pack_start (GTK_BOX (box), separator, FALSE, FALSE, 0);
+ gtk_widget_show (separator);
+
+ /* line and column numbers */
+ statusbar->position = gtk_label_new (NULL);
+ gtk_box_pack_start (GTK_BOX (box), statusbar->position, FALSE, TRUE, 0);
+ gtk_widget_show (statusbar->position);
+
+ /* separator */
+ separator = gtk_vseparator_new ();
+ gtk_box_pack_start (GTK_BOX (box), separator, FALSE, FALSE, 0);
+ gtk_widget_show (separator);
+
+ /* overwrite event box */
+ ebox = gtk_event_box_new ();
+ gtk_box_pack_start (GTK_BOX (box), ebox, FALSE, TRUE, 0);
+ gtk_event_box_set_visible_window (GTK_EVENT_BOX (ebox), FALSE);
+ mousepad_widget_set_tooltip_text (ebox, _("Toggle the overwrite mode"));
+ g_signal_connect (G_OBJECT (ebox), "button-press-event", G_CALLBACK (mousepad_statusbar_overwrite_clicked), statusbar);
+ gtk_widget_show (ebox);
+
+ /* overwrite label */
+ statusbar->overwrite = gtk_label_new (_("OVR"));
+ gtk_container_add (GTK_CONTAINER (ebox), statusbar->overwrite);
+ gtk_widget_show (statusbar->overwrite);
+}
+
+
+
+static gboolean
+mousepad_statusbar_overwrite_clicked (GtkWidget *widget,
+ GdkEventButton *event,
+ MousepadStatusbar *statusbar)
+{
+ mousepad_return_val_if_fail (MOUSEPAD_IS_STATUSBAR (statusbar), FALSE);
+
+ /* only respond on the left button click */
+ if (event->type != GDK_BUTTON_PRESS || event->button != 1)
+ return FALSE;
+
+ /* swap the overwrite mode */
+ statusbar->overwrite_enabled = !statusbar->overwrite_enabled;
+
+ /* send the signal */
+ g_signal_emit (G_OBJECT (statusbar), statusbar_signals[ENABLE_OVERWRITE], 0, statusbar->overwrite_enabled);
+
+ return TRUE;
+}
+
+
+
+static gboolean
+mousepad_statusbar_filetype_clicked (GtkWidget *widget,
+ GdkEventButton *event,
+ MousepadStatusbar *statusbar)
+{
+ GtkMenu *menu;
+ GList *children;
+ gint n_children = 0;
+
+ mousepad_return_val_if_fail (MOUSEPAD_IS_STATUSBAR (statusbar), FALSE);
+
+ /* only respond on the left button click */
+ if (event->type != GDK_BUTTON_PRESS || event->button != 1)
+ return FALSE;
+
+ /* create the popup menu */
+ menu = GTK_MENU (gtk_menu_new ());
+
+ /* send the signal to fill the menu */
+ g_signal_emit (G_OBJECT (statusbar), statusbar_signals[POPULATE_FILETYPE_POPUP], 0, menu);
+
+ /* get the number of items in the menu */
+ children = gtk_container_get_children (GTK_CONTAINER (menu));
+ n_children = g_list_length (children);
+ g_list_free (children);
+
+ /* make sure there's at least one item in the menu */
+ if (n_children)
+ {
+ /* cleanup the menu once a selection is made or the menu is cancelled */
+ g_signal_connect (menu, "selection-done", G_CALLBACK (gtk_widget_destroy), NULL);
+
+ /* show the menu */
+ gtk_menu_popup (menu, NULL, NULL, NULL, NULL, event->button, event->time);
+ }
+ else
+ {
+ /* since the menu wasn't shown, just destroy it straight-away */
+ gtk_widget_destroy (GTK_WIDGET (menu));
+ }
+
+ return TRUE;
+}
+
+
+
+void
+mousepad_statusbar_set_language (MousepadStatusbar *statusbar,
+ GtkSourceLanguage *language)
+{
+ gchar *label;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_STATUSBAR (statusbar));
+
+ if (language == NULL)
+ gtk_label_set_text (GTK_LABEL (statusbar->language), _("Filetype: None"));
+ else
+ {
+ label = g_strdup_printf (_("Filetype: %s"), gtk_source_language_get_name (language));
+ gtk_label_set_text (GTK_LABEL (statusbar->language), label);
+ g_free (label);
+ }
+}
+
+
+
+void
+mousepad_statusbar_set_cursor_position (MousepadStatusbar *statusbar,
+ gint line,
+ gint column,
+ gint selection)
+{
+ gchar string[64];
+
+ mousepad_return_if_fail (MOUSEPAD_IS_STATUSBAR (statusbar));
+
+ /* create printable string */
+ if (G_UNLIKELY (selection > 0))
+ g_snprintf (string, sizeof (string), _("Line: %d Column: %d Selection: %d"), line, column, selection);
+ else
+ g_snprintf (string, sizeof (string), _("Line: %d Column: %d"), line, column);
+
+ /* set label */
+ gtk_label_set_text (GTK_LABEL (statusbar->position), string);
+}
+
+
+
+void
+mousepad_statusbar_set_overwrite (MousepadStatusbar *statusbar,
+ gboolean overwrite)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_STATUSBAR (statusbar));
+
+ gtk_widget_set_sensitive (statusbar->overwrite, overwrite);
+
+ statusbar->overwrite_enabled = overwrite;
+}
diff --git a/mousepad/mousepad-statusbar.h b/mousepad/mousepad-statusbar.h
new file mode 100644
index 0000000..8ebe7a6
--- /dev/null
+++ b/mousepad/mousepad-statusbar.h
@@ -0,0 +1,49 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __MOUSEPAD_STATUSBAR_H__
+#define __MOUSEPAD_STATUSBAR_H__
+
+G_BEGIN_DECLS
+
+#define MOUSEPAD_TYPE_STATUSBAR (mousepad_statusbar_get_type ())
+#define MOUSEPAD_STATUSBAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MOUSEPAD_TYPE_STATUSBAR, MousepadStatusbar))
+#define MOUSEPAD_STATUSBAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MOUSEPAD_TYPE_STATUSBAR, MousepadStatusbarClass))
+#define MOUSEPAD_IS_STATUSBAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MOUSEPAD_TYPE_STATUSBAR))
+#define MOUSEPAD_IS_STATUSBAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MOUSEPAD_TYPE_STATUSBAR))
+#define MOUSEPAD_STATUSBAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MOUSEPAD_TYPE_STATUSBAR, MousepadStatusbarClass))
+
+typedef struct _MousepadStatusbarClass MousepadStatusbarClass;
+typedef struct _MousepadStatusbar MousepadStatusbar;
+
+GType mousepad_statusbar_get_type (void) G_GNUC_CONST;
+
+GtkWidget *mousepad_statusbar_new (void);
+
+void mousepad_statusbar_set_cursor_position (MousepadStatusbar *statusbar,
+ gint line,
+ gint column,
+ gint selection);
+
+void mousepad_statusbar_set_overwrite (MousepadStatusbar *statusbar,
+ gboolean overwrite);
+
+void mousepad_statusbar_set_language (MousepadStatusbar *statusbar,
+ GtkSourceLanguage *language);
+
+G_END_DECLS
+
+#endif /* !__MOUSEPAD_STATUSBAR_H__ */
diff --git a/mousepad/mousepad-util.c b/mousepad/mousepad-util.c
new file mode 100644
index 0000000..05ed8af
--- /dev/null
+++ b/mousepad/mousepad-util.c
@@ -0,0 +1,1206 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <mousepad/mousepad-private.h>
+#include <mousepad/mousepad-util.h>
+
+
+
+static gboolean
+mousepad_util_iter_word_characters (const GtkTextIter *iter)
+{
+ gunichar c;
+
+ /* get the characters */
+ c = gtk_text_iter_get_char (iter);
+
+ /* character we'd like to see in a word */
+ if (g_unichar_isalnum (c) || c == '_')
+ return TRUE;
+
+ return FALSE;
+}
+
+
+
+gboolean
+mousepad_util_iter_starts_word (const GtkTextIter *iter)
+{
+ GtkTextIter prev;
+
+ /* normal gtk word start */
+ if (!gtk_text_iter_starts_word (iter))
+ return FALSE;
+
+ /* init iter for previous char */
+ prev = *iter;
+
+ /* return true when we could not step backwards (start of buffer) */
+ if (!gtk_text_iter_backward_char (&prev))
+ return TRUE;
+
+ /* check if the previous char also belongs to the word */
+ if (mousepad_util_iter_word_characters (&prev))
+ return FALSE;
+
+ return TRUE;
+}
+
+
+
+gboolean
+mousepad_util_iter_ends_word (const GtkTextIter *iter)
+{
+ if (!gtk_text_iter_ends_word (iter))
+ return FALSE;
+
+ /* check if it's a character we'd like to see in a word */
+ if (mousepad_util_iter_word_characters (iter))
+ return FALSE;
+
+ return TRUE;
+}
+
+
+
+gboolean
+mousepad_util_iter_inside_word (const GtkTextIter *iter)
+{
+ GtkTextIter prev;
+
+ /* not inside a word when at beginning or end of a word */
+ if (mousepad_util_iter_starts_word (iter) || mousepad_util_iter_ends_word (iter))
+ return FALSE;
+
+ /* normal gtk function */
+ if (gtk_text_iter_inside_word (iter))
+ return TRUE;
+
+ /* check if the character is a word char */
+ if (!mousepad_util_iter_word_characters (iter))
+ return FALSE;
+
+ /* initialize previous iter */
+ prev = *iter;
+
+ /* get one character backwards */
+ if (!gtk_text_iter_backward_char (&prev))
+ return FALSE;
+
+ return mousepad_util_iter_word_characters (&prev);
+}
+
+
+
+gboolean
+mousepad_util_iter_forward_word_end (GtkTextIter *iter)
+{
+ if (mousepad_util_iter_ends_word (iter))
+ return TRUE;
+
+ while (gtk_text_iter_forward_char (iter))
+ if (mousepad_util_iter_ends_word (iter))
+ return TRUE;
+
+ return mousepad_util_iter_ends_word (iter);
+}
+
+
+
+gboolean
+mousepad_util_iter_backward_word_start (GtkTextIter *iter)
+{
+ /* return true if the iter already starts a word */
+ if (mousepad_util_iter_starts_word (iter))
+ return TRUE;
+
+ /* move backwards until we find a word start */
+ while (gtk_text_iter_backward_char (iter))
+ if (mousepad_util_iter_starts_word (iter))
+ return TRUE;
+
+ /* while stops when we hit the first char in the buffer */
+ return mousepad_util_iter_starts_word (iter);
+}
+
+
+
+gboolean
+mousepad_util_iter_forward_text_start (GtkTextIter *iter)
+{
+ mousepad_return_val_if_fail (!mousepad_util_iter_inside_word (iter), FALSE);
+
+ /* keep until we hit text or a line end */
+ while (g_unichar_isspace (gtk_text_iter_get_char (iter)))
+ if (gtk_text_iter_ends_line (iter) || !gtk_text_iter_forward_char (iter))
+ break;
+
+ return TRUE;
+}
+
+
+
+gboolean
+mousepad_util_iter_backward_text_start (GtkTextIter *iter)
+{
+ GtkTextIter prev = *iter;
+
+ mousepad_return_val_if_fail (!mousepad_util_iter_inside_word (iter), FALSE);
+
+ while (!gtk_text_iter_starts_line (&prev) && gtk_text_iter_backward_char (&prev))
+ {
+ if (g_unichar_isspace (gtk_text_iter_get_char (&prev)))
+ *iter = prev;
+ else
+ break;
+ }
+
+ return TRUE;
+}
+
+
+
+gchar *
+mousepad_util_config_name (const gchar *name)
+{
+ const gchar *s;
+ gchar *config, *t;
+ gboolean upper = TRUE;
+
+ /* allocate string */
+ config = g_new (gchar, strlen (name) + 1);
+
+ /* convert name */
+ for (s = name, t = config; *s != '\0'; ++s)
+ {
+ if (*s == '-')
+ {
+ upper = TRUE;
+ }
+ else if (upper)
+ {
+ *t++ = g_ascii_toupper (*s);
+ upper = FALSE;
+ }
+ else
+ {
+ *t++ = g_ascii_tolower (*s);
+ }
+ }
+
+ /* zerro terminate string */
+ *t = '\0';
+
+ return config;
+}
+
+
+
+gchar *
+mousepad_util_key_name (const gchar *name)
+{
+ const gchar *s;
+ gchar *key, *t;
+
+ /* allocate string (max of 9 extra - chars) */
+ key = g_new (gchar, strlen (name) + 10);
+
+ /* convert name */
+ for (s = name, t = key; *s != '\0'; ++s)
+ {
+ if (g_ascii_isupper (*s) && s != name)
+ *t++ = '-';
+
+ *t++ = g_ascii_tolower (*s);
+ }
+
+ /* zerro terminate string */
+ *t = '\0';
+
+ return key;
+}
+
+
+
+gchar *
+mousepad_util_utf8_strcapital (const gchar *str)
+{
+ gunichar c;
+ const gchar *p;
+ gchar *buf;
+ GString *result;
+ gboolean upper = TRUE;
+
+ mousepad_return_val_if_fail (g_utf8_validate (str, -1, NULL), NULL);
+
+ /* create a new string */
+ result = g_string_sized_new (strlen (str));
+
+ /* walk though the string */
+ for (p = str; *p != '\0'; p = g_utf8_next_char (p))
+ {
+ /* get the unicode char */
+ c = g_utf8_get_char (p);
+
+ /* only change the case of alpha chars */
+ if (g_unichar_isalpha (c))
+ {
+ /* check case */
+ if (upper ? g_unichar_isupper (c) : g_unichar_islower (c))
+ {
+ /* currect case is already correct */
+ g_string_append_unichar (result, c);
+ }
+ else
+ {
+ /* convert the case of the char and append it */
+ buf = upper ? g_utf8_strup (p, 1) : g_utf8_strdown (p, 1);
+ g_string_append (result, buf);
+ g_free (buf);
+ }
+
+ /* next char must be lowercase */
+ upper = FALSE;
+ }
+ else
+ {
+ /* append the char */
+ g_string_append_unichar (result, c);
+
+ /* next alpha char uppercase after a space */
+ upper = g_unichar_isspace (c);
+ }
+ }
+
+ /* return the result */
+ return g_string_free (result, FALSE);
+}
+
+
+
+gchar *
+mousepad_util_utf8_stropposite (const gchar *str)
+{
+ gunichar c;
+ const gchar *p;
+ gchar *buf;
+ GString *result;
+
+ mousepad_return_val_if_fail (g_utf8_validate (str, -1, NULL), NULL);
+
+ /* create a new string */
+ result = g_string_sized_new (strlen (str));
+
+ /* walk though the string */
+ for (p = str; *p != '\0'; p = g_utf8_next_char (p))
+ {
+ /* get the unicode char */
+ c = g_utf8_get_char (p);
+
+ /* only change the case of alpha chars */
+ if (g_unichar_isalpha (c))
+ {
+ /* get the opposite case of the char */
+ if (g_unichar_isupper (c))
+ buf = g_utf8_strdown (p, 1);
+ else
+ buf = g_utf8_strup (p, 1);
+
+ /* append to the buffer */
+ g_string_append (result, buf);
+ g_free (buf);
+ }
+ else
+ {
+ /* append the char */
+ g_string_append_unichar (result, c);
+ }
+ }
+
+ /* return the result */
+ return g_string_free (result, FALSE);
+}
+
+
+
+gchar *
+mousepad_util_escape_underscores (const gchar *str)
+{
+ GString *result;
+ const gchar *s;
+
+ /* allocate a new string */
+ result = g_string_sized_new (strlen (str));
+
+ /* escape all underscores */
+ for (s = str; *s != '\0'; ++s)
+ {
+ if (G_UNLIKELY (*s == '_'))
+ g_string_append (result, "__");
+ else
+ g_string_append_c (result, *s);
+ }
+
+ return g_string_free (result, FALSE);
+}
+
+
+
+GtkWidget *
+mousepad_util_image_button (const gchar *stock_id,
+ const gchar *label)
+{
+ GtkWidget *button, *image;
+
+ image = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_BUTTON);
+ gtk_widget_show (image);
+
+ button = gtk_button_new_with_mnemonic (label);
+ gtk_button_set_image (GTK_BUTTON (button), image);
+ gtk_widget_show (button);
+
+ return button;
+}
+
+
+
+void
+mousepad_util_entry_error (GtkWidget *widget,
+ gboolean error)
+{
+ const GdkColor red = {0, 0xffff, 0x6666, 0x6666};
+ const GdkColor white = {0, 0xffff, 0xffff, 0xffff};
+ gpointer pointer;
+
+ mousepad_return_if_fail (GTK_IS_WIDGET (widget));
+
+ /* get the current error state */
+ pointer = mousepad_object_get_data (G_OBJECT (widget), "error-state");
+
+ /* only change the state when really needed to avoid multiple widget calls */
+ if (GPOINTER_TO_INT (pointer) != error)
+ {
+ /* set the widget style */
+ gtk_widget_modify_base (widget, GTK_STATE_NORMAL, error ? &red : NULL);
+ gtk_widget_modify_text (widget, GTK_STATE_NORMAL, error ? &white : NULL);
+
+ /* set the new state */
+ mousepad_object_set_data (G_OBJECT (widget), "error-state", GINT_TO_POINTER (error));
+ }
+}
+
+
+
+void
+mousepad_util_dialog_header (GtkDialog *dialog,
+ const gchar *title,
+ const gchar *subtitle,
+ const gchar *iconname)
+{
+ gchar *full_title;
+ GtkWidget *vbox, *ebox, *hbox;
+ GtkWidget *icon, *label, *line;
+
+ /* remove the main vbox */
+ g_object_ref (G_OBJECT (dialog->vbox));
+ gtk_container_remove (GTK_CONTAINER (dialog), dialog->vbox);
+
+ /* add a new vbox to the main window */
+ vbox = gtk_vbox_new (FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (dialog), vbox);
+ gtk_widget_show (vbox);
+
+ /* event box for the background color */
+ ebox = gtk_event_box_new ();
+ gtk_box_pack_start (GTK_BOX (vbox), ebox, FALSE, FALSE, 0);
+ gtk_widget_modify_bg (ebox, GTK_STATE_NORMAL, &ebox->style->base[GTK_STATE_NORMAL]);
+ gtk_widget_show (ebox);
+
+ /* create a hbox */
+ hbox = gtk_hbox_new (FALSE, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (hbox), 6);
+ gtk_container_add (GTK_CONTAINER (ebox), hbox);
+ gtk_widget_show (hbox);
+
+ /* title icon */
+ icon = gtk_image_new_from_icon_name (iconname, GTK_ICON_SIZE_DIALOG);
+ gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
+ gtk_widget_show (icon);
+
+ /* create the title */
+ full_title = g_strdup_printf ("<b><big>%s</big></b>\n%s", title, subtitle);
+
+ /* title label */
+ label = gtk_label_new (full_title);
+ gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+ gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
+ gtk_widget_show (label);
+
+ /* cleanup */
+ g_free (full_title);
+
+ /* add the separator between header and content */
+ line = gtk_hseparator_new ();
+ gtk_box_pack_start (GTK_BOX (vbox), line, FALSE, FALSE, 0);
+ gtk_widget_show (line);
+
+ /* add the main dialog box to the new vbox */
+ gtk_box_pack_start (GTK_BOX (vbox), GTK_DIALOG (dialog)->vbox, TRUE, TRUE, 0);
+ g_object_unref (G_OBJECT (GTK_DIALOG (dialog)->vbox));
+}
+
+
+#if !GTK_CHECK_VERSION (2,12,0)
+void
+mousepad_util_set_tooltip (GtkWidget *widget,
+ const gchar *string)
+{
+ static GtkTooltips *tooltips = NULL;
+
+ mousepad_return_if_fail (GTK_IS_WIDGET (widget));
+ mousepad_return_if_fail (string ? g_utf8_validate (string, -1, NULL) : TRUE);
+
+ /* allocate the shared tooltips on-demand */
+ if (G_UNLIKELY (tooltips == NULL))
+ tooltips = gtk_tooltips_new ();
+
+ /* setup the tooltip for the widget */
+ gtk_tooltips_set_tip (tooltips, widget, string, NULL);
+}
+#endif
+
+
+gint
+mousepad_util_get_real_line_offset (const GtkTextIter *iter,
+ gint tab_size)
+{
+ gint offset = 0;
+ GtkTextIter needle = *iter;
+
+ /* move the needle to the start of the line */
+ gtk_text_iter_set_line_offset (&needle, 0);
+
+ /* forward the needle until we hit the iter */
+ while (!gtk_text_iter_equal (&needle, iter))
+ {
+ /* append the real tab offset or 1 */
+ if (gtk_text_iter_get_char (&needle) == '\t')
+ offset += (tab_size - (offset % tab_size));
+ else
+ offset++;
+
+ /* next char */
+ gtk_text_iter_forward_char (&needle);
+ }
+
+ return offset;
+}
+
+
+
+gboolean
+mousepad_util_forward_iter_to_text (GtkTextIter *iter,
+ const GtkTextIter *limit)
+{
+ gunichar c;
+
+ do
+ {
+ /* get the iter character */
+ c = gtk_text_iter_get_char (iter);
+
+ /* break if the character is not a space */
+ if (!g_unichar_isspace (c) || c == '\n' || c == '\r')
+ break;
+
+ /* break when we reached the limit iter */
+ if (limit && gtk_text_iter_equal (iter, limit))
+ return FALSE;
+ }
+ while (gtk_text_iter_forward_char (iter));
+
+ return TRUE;
+}
+
+
+
+gchar *
+mousepad_util_get_save_location (const gchar *relpath,
+ gboolean create_parents)
+{
+ gchar *filename, *dirname;
+
+ mousepad_return_val_if_fail (g_get_user_config_dir () != NULL, NULL);
+
+ /* create the full filename */
+ filename = g_build_filename (g_get_user_config_dir (), relpath, NULL);
+
+ /* test if the file exists */
+ if (G_UNLIKELY (g_file_test (filename, G_FILE_TEST_EXISTS) == FALSE))
+ {
+ if (create_parents)
+ {
+ /* get the directory name */
+ dirname = g_path_get_dirname (filename);
+
+ /* make the directory with parents */
+ if (g_mkdir_with_parents (dirname, 0700) == -1)
+ {
+ /* show warning to the user */
+ g_critical (_("Unable to create base directory \"%s\". "
+ "Saving to file \"%s\" will be aborted."), dirname, filename);
+
+ /* don't return a filename, to avoid problems */
+ g_free (filename);
+ filename = NULL;
+ }
+
+ /* cleanup */
+ g_free (dirname);
+ }
+ else
+ {
+ /* cleanup */
+ g_free (filename);
+ filename = NULL;
+ }
+ }
+
+ return filename;
+}
+
+
+
+void
+mousepad_util_save_key_file (GKeyFile *keyfile,
+ const gchar *filename)
+{
+ gchar *contents;
+ gsize length;
+ GError *error = NULL;
+
+ /* get the contents of the key file */
+ contents = g_key_file_to_data (keyfile, &length, &error);
+
+ if (G_LIKELY (error == NULL))
+ {
+ /* write the contents to the file */
+ if (G_UNLIKELY (g_file_set_contents (filename, contents, length, &error) == FALSE))
+ goto print_error;
+ }
+ else
+ {
+ print_error:
+
+ /* print error */
+ g_critical (_("Failed to store the preferences to \"%s\": %s"), filename, error->message);
+
+ /* cleanup */
+ g_error_free (error);
+ }
+
+ /* cleanup */
+ g_free (contents);
+}
+
+
+
+GType
+mousepad_util_search_flags_get_type (void)
+{
+ static GType type = G_TYPE_NONE;
+
+ if (G_UNLIKELY (type == G_TYPE_NONE))
+ {
+ /* use empty values table */
+ static const GFlagsValue values[] =
+ {
+ { 0, NULL, NULL }
+ };
+
+ /* register the type */
+ type = g_flags_register_static (I_("MousepadSearchFlags"), values);
+ }
+
+ return type;
+}
+
+
+
+static gboolean
+mousepad_util_search_iter (const GtkTextIter *start,
+ const gchar *string,
+ MousepadSearchFlags flags,
+ GtkTextIter *match_start,
+ GtkTextIter *match_end,
+ const GtkTextIter *limit)
+{
+ GtkTextIter iter, begin;
+ gunichar iter_char, str_char;
+ gboolean succeed = FALSE;
+ const gchar *needle = string;
+ gboolean match_case, search_backwards, whole_word;
+ guint needle_offset = 0;
+
+ mousepad_return_val_if_fail (start != NULL, FALSE);
+ mousepad_return_val_if_fail (string != NULL, FALSE);
+ mousepad_return_val_if_fail (limit != NULL, FALSE);
+
+ /* search properties */
+ match_case = (flags & MOUSEPAD_SEARCH_FLAGS_MATCH_CASE) != 0;
+ search_backwards = (flags & MOUSEPAD_SEARCH_FLAGS_DIR_BACKWARD) != 0;
+ whole_word = (flags & MOUSEPAD_SEARCH_FLAGS_WHOLE_WORD) != 0;
+
+ /* set the start iter */
+ iter = *start;
+
+ /* walk from the start to the end iter */
+ for (;;)
+ {
+ /* break when we reach the limit iter */
+ if (G_UNLIKELY (gtk_text_iter_equal (&iter, limit)))
+ break;
+
+ /* get the unichar characters */
+ iter_char = gtk_text_iter_get_char (&iter);
+ str_char = g_utf8_get_char (needle);
+
+ /* skip unknown characters */
+ if (G_UNLIKELY (iter_char == 0xFFFC))
+ goto continue_searching;
+
+ /* lower case searching */
+ if (!match_case)
+ {
+ /* convert the characters to lower case */
+ iter_char = g_unichar_tolower (iter_char);
+ str_char = g_unichar_tolower (str_char);
+ }
+
+ /* compare the two characters */
+ if (iter_char == str_char)
+ {
+ /* first character matched, set the begin iter */
+ if (needle_offset == 0)
+ begin = iter;
+
+ /* get the next character and increase the offset counter */
+ needle = g_utf8_next_char (needle);
+ needle_offset++;
+
+ /* we hit the end of the search string, so we had a full match */
+ if (G_UNLIKELY (*needle == '\0'))
+ {
+ if (G_LIKELY (!search_backwards))
+ {
+ /* set the end iter after the character (for selection) */
+ gtk_text_iter_forward_char (&iter);
+
+ /* check if we match a whole word */
+ if (whole_word && !(mousepad_util_iter_starts_word (&begin) && mousepad_util_iter_ends_word (&iter)))
+ goto reset_and_continue_searching;
+ }
+ else
+ {
+ /* set the start iter after the character (for selection) */
+ gtk_text_iter_forward_char (&begin);
+
+ /* check if we match a whole word */
+ if (whole_word && !(mousepad_util_iter_starts_word (&iter) && mousepad_util_iter_ends_word (&begin)))
+ goto reset_and_continue_searching;
+ }
+
+ /* set the start and end iters */
+ *match_start = begin;
+ *match_end = iter;
+
+ /* return true and stop searching */
+ succeed = TRUE;
+
+ break;
+ }
+ }
+ else if (needle_offset > 0)
+ {
+ /* label */
+ reset_and_continue_searching:
+
+ /* reset the needle */
+ needle = string;
+ needle_offset = 0;
+
+ /* reset the iter */
+ iter = begin;
+ }
+
+ /* label */
+ continue_searching:
+
+ /* jump to next iter in the buffer */
+ if ((!search_backwards && !gtk_text_iter_forward_char (&iter))
+ || (search_backwards && !gtk_text_iter_backward_char (&iter)))
+ break;
+ }
+
+ return succeed;
+}
+
+
+
+static void
+mousepad_util_search_get_iters (GtkTextBuffer *buffer,
+ MousepadSearchFlags flags,
+ GtkTextIter *start,
+ GtkTextIter *end,
+ GtkTextIter *iter)
+{
+ GtkTextIter sel_start, sel_end, tmp;
+
+ /* get selection bounds */
+ gtk_text_buffer_get_selection_bounds (buffer, &sel_start, &sel_end);
+
+ if (flags & MOUSEPAD_SEARCH_FLAGS_AREA_DOCUMENT)
+ {
+ /* get document bounds */
+ gtk_text_buffer_get_bounds (buffer, start, end);
+
+ /* set the start iter */
+ if (flags & MOUSEPAD_SEARCH_FLAGS_ITER_AREA_START)
+ *iter = *start;
+ else if (flags & MOUSEPAD_SEARCH_FLAGS_ITER_AREA_END)
+ *iter = *end;
+ else
+ goto set_selection_iter;
+ }
+ else if (flags & MOUSEPAD_SEARCH_FLAGS_AREA_SELECTION)
+ {
+ /* set area iters */
+ *start = sel_start;
+ *end = sel_end;
+
+ set_selection_iter:
+
+ /* set the start iter */
+ if (flags & (MOUSEPAD_SEARCH_FLAGS_ITER_AREA_START | MOUSEPAD_SEARCH_FLAGS_ITER_SEL_START))
+ *iter = sel_start;
+ else if (flags & (MOUSEPAD_SEARCH_FLAGS_ITER_AREA_END | MOUSEPAD_SEARCH_FLAGS_ITER_SEL_END))
+ *iter = sel_end;
+ else
+ mousepad_assert_not_reached ();
+ }
+ else
+ {
+ /* this should never happen */
+ mousepad_assert_not_reached ();
+ }
+
+ /* invert the start and end iter on backwards searching */
+ if (flags & MOUSEPAD_SEARCH_FLAGS_DIR_BACKWARD)
+ {
+ tmp = *start;
+ *start = *end;
+ *end = tmp;
+ }
+}
+
+
+
+gint
+mousepad_util_highlight (GtkTextBuffer *buffer,
+ GtkTextTag *tag,
+ const gchar *string,
+ MousepadSearchFlags flags)
+{
+ GtkTextIter start, iter, end;
+ GtkTextIter match_start, match_end;
+ GtkTextIter cache_start, cache_end;
+ gboolean found, cached = FALSE;
+ gint counter = 0;
+
+ mousepad_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), -1);
+ mousepad_return_val_if_fail (GTK_IS_TEXT_TAG (tag), -1);
+ mousepad_return_val_if_fail (string == NULL || g_utf8_validate (string, -1, NULL), -1);
+ mousepad_return_val_if_fail ((flags & MOUSEPAD_SEARCH_FLAGS_DIR_BACKWARD) == 0, -1);
+
+ /* get the buffer bounds */
+ gtk_text_buffer_get_bounds (buffer, &start, &end);
+
+ /* remove all the highlight tags */
+ gtk_text_buffer_remove_tag (buffer, tag, &start, &end);
+
+ /* quit if there is nothing to highlight */
+ if (string == NULL || *string == '\0' || flags & MOUSEPAD_SEARCH_FLAGS_ACTION_CLEANUP)
+ return 0;
+
+ /* get the search iters */
+ mousepad_util_search_get_iters (buffer, flags, &start, &end, &iter);
+
+ /* initialize cache iters */
+ cache_start = cache_end = iter;
+
+ /* highlight all the occurences of the strings */
+ do
+ {
+ /* search for the next occurence of the string */
+ found = mousepad_util_search_iter (&iter, string, flags, &match_start, &match_end, &end);
+
+ if (G_LIKELY (found))
+ {
+ /* try to extend the cache */
+ if (gtk_text_iter_equal (&cache_end, &match_start))
+ {
+ /* enable caching */
+ cached = TRUE;
+ }
+ else
+ {
+ if (cached)
+ {
+ /* highlight the cached occurences */
+ gtk_text_buffer_apply_tag (buffer, tag, &cache_start, &cache_end);
+
+ /* cache is flushed */
+ cached = FALSE;
+ }
+
+ /* highlight the matched occurence */
+ gtk_text_buffer_apply_tag (buffer, tag, &match_start, &match_end);
+
+ /* set the new cache start iter */
+ cache_start = match_start;
+ }
+
+ /* set the end iters */
+ iter = cache_end = match_end;
+
+ /* increase the counter */
+ counter++;
+ }
+ else if (G_UNLIKELY (cached))
+ {
+ /* flush the cached iters */
+ gtk_text_buffer_apply_tag (buffer, tag, &cache_start, &cache_end);
+ }
+ }
+ while (G_LIKELY (found));
+
+ return counter;
+}
+
+
+
+gint
+mousepad_util_search (GtkTextBuffer *buffer,
+ const gchar *string,
+ const gchar *replace,
+ MousepadSearchFlags flags)
+{
+ gchar *reversed = NULL;
+ gint counter = 0;
+ gboolean found, search_again = FALSE;
+ gboolean search_backwards, wrap_around;
+ GtkTextIter start, end, iter;
+ GtkTextIter match_start, match_end;
+ GtkTextMark *mark_start, *mark_iter, *mark_end, *mark_replace;
+
+ mousepad_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), -1);
+ mousepad_return_val_if_fail (string && g_utf8_validate (string, -1, NULL), -1);
+ mousepad_return_val_if_fail (replace == NULL || g_utf8_validate (replace, -1, NULL), -1);
+ mousepad_return_val_if_fail ((flags & MOUSEPAD_SEARCH_FLAGS_ACTION_HIGHTLIGHT) == 0, -1);
+
+ /* freeze buffer notifications */
+ g_object_freeze_notify (G_OBJECT (buffer));
+
+ /* get the search iters */
+ mousepad_util_search_get_iters (buffer, flags, &start, &end, &iter);
+
+ /* store the initial iters in marks */
+ mark_start = gtk_text_buffer_create_mark (buffer, NULL, &start, TRUE);
+ mark_iter = gtk_text_buffer_create_mark (buffer, NULL, &iter, TRUE);
+ mark_end = gtk_text_buffer_create_mark (buffer, NULL, &end, TRUE);
+
+ /* some to make the code easier to read */
+ search_backwards = ((flags & MOUSEPAD_SEARCH_FLAGS_DIR_BACKWARD) != 0);
+ wrap_around = ((flags & MOUSEPAD_SEARCH_FLAGS_WRAP_AROUND) != 0 && !gtk_text_iter_equal (&start, &iter));
+
+ /* if we're not really searching anything, reset the cursor */
+ if (string == NULL || *string == '\0')
+ goto reset_cursor;
+
+ if (search_backwards)
+ {
+ /* reverse the search string */
+ reversed = g_utf8_strreverse (string, -1);
+
+ /* set the new search string */
+ string = reversed;
+ }
+
+ do
+ {
+ /* search the string */
+ found = mousepad_util_search_iter (&iter, string, flags, &match_start, &match_end, &end);
+
+ /* don't search again unless changed below */
+ search_again = FALSE;
+
+ if (found)
+ {
+ /* handle the action */
+ if (flags & MOUSEPAD_SEARCH_FLAGS_ACTION_SELECT)
+ {
+ /* select the match */
+ gtk_text_buffer_select_range (buffer, &match_start, &match_end);
+ }
+ else if (flags & MOUSEPAD_SEARCH_FLAGS_ACTION_REPLACE)
+ {
+ /* create text mark */
+ mark_replace = gtk_text_buffer_create_mark (buffer, NULL, &match_start, search_backwards);
+
+ /* delete the match */
+ gtk_text_buffer_delete (buffer, &match_start, &match_end);
+
+ /* restore the iter */
+ gtk_text_buffer_get_iter_at_mark (buffer, &iter, mark_replace);
+
+ /* insert the replacement */
+ if (G_LIKELY (replace))
+ gtk_text_buffer_insert (buffer, &iter, replace, -1);
+
+ /* remove the mark */
+ gtk_text_buffer_delete_mark (buffer, mark_replace);
+
+ /* restore the begin and end iters */
+ gtk_text_buffer_get_iter_at_mark (buffer, &start, mark_start);
+ gtk_text_buffer_get_iter_at_mark (buffer, &end, mark_end);
+
+ /* when we don't replace all matches, select the next one */
+ if ((flags & MOUSEPAD_SEARCH_FLAGS_ENTIRE_AREA) == 0)
+ flags |= MOUSEPAD_SEARCH_FLAGS_ACTION_SELECT;
+
+ /* search again */
+ search_again = TRUE;
+ }
+ else if (flags & MOUSEPAD_SEARCH_FLAGS_ACTION_NONE)
+ {
+ /* keep searching when requested */
+ if (flags & MOUSEPAD_SEARCH_FLAGS_ENTIRE_AREA)
+ search_again = TRUE;
+
+ /* move iter */
+ iter = match_end;
+ }
+ else
+ {
+ /* no valid action was defined */
+ mousepad_assert_not_reached ();
+ }
+
+ /* increase the counter */
+ counter++;
+ }
+ else if (wrap_around)
+ {
+ /* get the start iter (note that the start and iter were already
+ * reversed for backwards searching) */
+ gtk_text_buffer_get_iter_at_mark (buffer, &iter, mark_start);
+
+ /* get end iter */
+ gtk_text_buffer_get_iter_at_mark (buffer, &end, mark_iter);
+
+ /* we wrapped, don't try again */
+ wrap_around = FALSE;
+
+ /* search again */
+ search_again = TRUE;
+ }
+ else
+ {
+ reset_cursor:
+
+ /* get the initial start mark */
+ gtk_text_buffer_get_iter_at_mark (buffer, &iter, mark_iter);
+
+ /* reset the cursor */
+ gtk_text_buffer_place_cursor (buffer, &iter);
+ }
+ }
+ while (search_again);
+
+ /* make sure the selection is restored */
+ if (flags & MOUSEPAD_SEARCH_FLAGS_AREA_SELECTION)
+ gtk_text_buffer_select_range (buffer, &start, &end);
+
+ /* cleanup */
+ g_free (reversed);
+
+ /* cleanup marks */
+ gtk_text_buffer_delete_mark (buffer, mark_start);
+ gtk_text_buffer_delete_mark (buffer, mark_iter);
+ gtk_text_buffer_delete_mark (buffer, mark_end);
+
+ /* thawn buffer notifications */
+ g_object_thaw_notify (G_OBJECT (buffer));
+
+ return counter;
+}
+
+
+
+/**
+ * Colour scheme functions
+ */
+gint
+mousepad_util_color_schemes_name_compare (gconstpointer a,
+ gconstpointer b)
+{
+ const gchar *name_a, *name_b;
+
+ if (G_UNLIKELY (!GTK_IS_SOURCE_STYLE_SCHEME (a)))
+ return -(a != b);
+ if (G_UNLIKELY (!GTK_IS_SOURCE_STYLE_SCHEME (b)))
+ return a != b;
+
+ name_a = gtk_source_style_scheme_get_name (GTK_SOURCE_STYLE_SCHEME (a));
+ name_b = gtk_source_style_scheme_get_name (GTK_SOURCE_STYLE_SCHEME (b));
+
+ return g_utf8_collate (name_a, name_b);
+}
+
+
+
+GSList *
+mousepad_util_color_schemes_get (void)
+{
+ GSList *list = NULL;
+ const gchar * const *schemes;
+ GtkSourceStyleScheme *scheme;
+
+ schemes = gtk_source_style_scheme_manager_get_scheme_ids (
+ gtk_source_style_scheme_manager_get_default ());
+
+ while (*schemes)
+ {
+ scheme = gtk_source_style_scheme_manager_get_scheme (
+ gtk_source_style_scheme_manager_get_default (), *schemes);
+ list = g_slist_prepend (list, scheme);
+ schemes++;
+ }
+
+ return list;
+}
+
+
+
+GSList *
+mousepad_util_color_schemes_get_sorted (void)
+{
+ return g_slist_sort (mousepad_util_color_schemes_get (),
+ mousepad_util_color_schemes_name_compare);
+}
+
+
+
+/**
+ * Language/filetype functions
+ */
+gint
+mousepad_util_languages_name_compare (gconstpointer a,
+ gconstpointer b)
+{
+ const gchar *name_a, *name_b;
+
+ if (G_UNLIKELY (!GTK_IS_SOURCE_LANGUAGE (a)))
+ return -(a != b);
+ if (G_UNLIKELY (!GTK_IS_SOURCE_LANGUAGE (b)))
+ return a != b;
+
+ name_a = gtk_source_language_get_name (GTK_SOURCE_LANGUAGE (a));
+ name_b = gtk_source_language_get_name (GTK_SOURCE_LANGUAGE (b));
+
+ return g_utf8_collate (name_a, name_b);
+}
+
+
+
+GSList *
+mousepad_util_language_sections_get_sorted (void)
+{
+ GSList *list = NULL;
+ const gchar *const *languages;
+ GtkSourceLanguage *language;
+ GtkSourceLanguageManager *manager;
+
+ manager = gtk_source_language_manager_get_default ();
+ languages = gtk_source_language_manager_get_language_ids (manager);
+
+ while (*languages)
+ {
+ language = gtk_source_language_manager_get_language (manager, *languages);
+ if (G_LIKELY (GTK_IS_SOURCE_LANGUAGE (language)))
+ {
+ /* ensure no duplicates in list */
+ if (!g_slist_find_custom (list,
+ gtk_source_language_get_section (language),
+ (GCompareFunc)g_strcmp0))
+ {
+ list = g_slist_prepend (list, (gchar *)gtk_source_language_get_section (language));
+ }
+ }
+ languages++;
+ }
+
+ return g_slist_sort (list, (GCompareFunc)g_strcmp0);
+}
+
+
+
+GSList *
+mousepad_util_languages_get_sorted_for_section (const gchar *section)
+{
+ GSList *list = NULL;
+ const gchar *const *languages;
+ GtkSourceLanguage *language;
+ GtkSourceLanguageManager *manager;
+
+ mousepad_return_val_if_fail (section != NULL, NULL);
+
+ manager = gtk_source_language_manager_get_default ();
+ languages = gtk_source_language_manager_get_language_ids (manager);
+
+ while (*languages)
+ {
+ language = gtk_source_language_manager_get_language (manager, *languages);
+ if (G_LIKELY (GTK_IS_SOURCE_LANGUAGE (language)))
+ {
+ /* only get languages in the specified section */
+ if (g_strcmp0 (gtk_source_language_get_section (language), section) == 0)
+ list = g_slist_prepend (list, language);
+ }
+ languages++;
+ }
+
+ return g_slist_sort(list, (GCompareFunc)mousepad_util_languages_name_compare);
+}
diff --git a/mousepad/mousepad-util.h b/mousepad/mousepad-util.h
new file mode 100644
index 0000000..dbafd81
--- /dev/null
+++ b/mousepad/mousepad-util.h
@@ -0,0 +1,138 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __MOUSEPAD_UTIL_H__
+#define __MOUSEPAD_UTIL_H__
+
+G_BEGIN_DECLS
+
+#define MOUSEPAD_TYPE_SEARCH_FLAGS (mousepad_util_search_flags_get_type ())
+
+typedef enum _MousepadSearchFlags MousepadSearchFlags;
+
+enum _MousepadSearchFlags
+{
+ /* search area */
+ MOUSEPAD_SEARCH_FLAGS_AREA_DOCUMENT = 1 << 0, /* search the entire document */
+ MOUSEPAD_SEARCH_FLAGS_AREA_SELECTION = 1 << 1, /* search inside selection */
+
+ /* iter start point */
+ MOUSEPAD_SEARCH_FLAGS_ITER_AREA_START = 1 << 2, /* search from the beginning of the area */
+ MOUSEPAD_SEARCH_FLAGS_ITER_AREA_END = 1 << 3, /* search from the end of the area */
+ MOUSEPAD_SEARCH_FLAGS_ITER_SEL_START = 1 << 4, /* start at from the beginning of the selection */
+ MOUSEPAD_SEARCH_FLAGS_ITER_SEL_END = 1 << 5, /* start at from the end of the selection */
+
+ /* direction */
+ MOUSEPAD_SEARCH_FLAGS_DIR_FORWARD = 1 << 6, /* search forward to end of area */
+ MOUSEPAD_SEARCH_FLAGS_DIR_BACKWARD = 1 << 7, /* search backwards to start of area */
+
+ /* search settings */
+ MOUSEPAD_SEARCH_FLAGS_MATCH_CASE = 1 << 8, /* match case */
+ MOUSEPAD_SEARCH_FLAGS_WHOLE_WORD = 1 << 9, /* only match whole words */
+ MOUSEPAD_SEARCH_FLAGS_WRAP_AROUND = 1 << 10, /* wrap around */
+ MOUSEPAD_SEARCH_FLAGS_ENTIRE_AREA = 1 << 11, /* keep searching until the end of the area is reached */
+ MOUSEPAD_SEARCH_FLAGS_ALL_DOCUMENTS = 1 << 12, /* search all documents */
+
+
+ /* actions */
+ MOUSEPAD_SEARCH_FLAGS_ACTION_NONE = 1 << 13, /* no visible actions */
+ MOUSEPAD_SEARCH_FLAGS_ACTION_HIGHTLIGHT = 1 << 14, /* highlight all the occurences */
+ MOUSEPAD_SEARCH_FLAGS_ACTION_CLEANUP = 1 << 15, /* cleanup the highlighted occurences */
+ MOUSEPAD_SEARCH_FLAGS_ACTION_SELECT = 1 << 16, /* select the match */
+ MOUSEPAD_SEARCH_FLAGS_ACTION_REPLACE = 1 << 17, /* replace the match */
+};
+
+gboolean mousepad_util_iter_starts_word (const GtkTextIter *iter);
+
+gboolean mousepad_util_iter_ends_word (const GtkTextIter *iter);
+
+gboolean mousepad_util_iter_inside_word (const GtkTextIter *iter);
+
+gboolean mousepad_util_iter_forward_word_end (GtkTextIter *iter);
+
+gboolean mousepad_util_iter_backward_word_start (GtkTextIter *iter);
+
+gboolean mousepad_util_iter_forward_text_start (GtkTextIter *iter);
+
+gboolean mousepad_util_iter_backward_text_start (GtkTextIter *iter);
+
+gchar *mousepad_util_config_name (const gchar *name);
+
+gchar *mousepad_util_key_name (const gchar *name);
+
+gchar *mousepad_util_utf8_strcapital (const gchar *str);
+
+gchar *mousepad_util_utf8_stropposite (const gchar *str);
+
+gchar *mousepad_util_escape_underscores (const gchar *str);
+
+GtkWidget *mousepad_util_image_button (const gchar *stock_id,
+ const gchar *label);
+
+void mousepad_util_entry_error (GtkWidget *widget,
+ gboolean error);
+
+void mousepad_util_dialog_header (GtkDialog *dialog,
+ const gchar *title,
+ const gchar *subtitle,
+ const gchar *icon);
+
+#if !GTK_CHECK_VERSION (2,12,0)
+void mousepad_util_set_tooltip (GtkWidget *widget,
+ const gchar *string);
+#endif
+
+gint mousepad_util_get_real_line_offset (const GtkTextIter *iter,
+ gint tab_size);
+
+gboolean mousepad_util_forward_iter_to_text (GtkTextIter *iter,
+ const GtkTextIter *limit);
+
+gchar *mousepad_util_get_save_location (const gchar *relpath,
+ gboolean create_parents);
+
+void mousepad_util_save_key_file (GKeyFile *keyfile,
+ const gchar *filename);
+
+GType mousepad_util_search_flags_get_type (void) G_GNUC_CONST;
+
+gint mousepad_util_highlight (GtkTextBuffer *buffer,
+ GtkTextTag *tag,
+ const gchar *string,
+ MousepadSearchFlags flags);
+
+gint mousepad_util_search (GtkTextBuffer *buffer,
+ const gchar *string,
+ const gchar *replace,
+ MousepadSearchFlags flags);
+
+gint mousepad_util_color_schemes_name_compare (gconstpointer a,
+ gconstpointer b);
+
+GSList *mousepad_util_color_schemes_get (void);
+
+GSList *mousepad_util_color_schemes_get_sorted (void);
+
+gint mousepad_util_languages_name_compare (gconstpointer a,
+ gconstpointer b);
+
+GSList *mousepad_util_language_sections_get_sorted (void);
+
+GSList *mousepad_util_languages_get_sorted_for_section (const gchar *section);
+
+G_END_DECLS
+
+#endif /* !__MOUSEPAD_UTIL_H__ */
diff --git a/mousepad/mousepad-view.c b/mousepad/mousepad-view.c
new file mode 100644
index 0000000..9bb4ace
--- /dev/null
+++ b/mousepad/mousepad-view.c
@@ -0,0 +1,2402 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <gdk/gdkkeysyms.h>
+
+#include <mousepad/mousepad-private.h>
+#include <mousepad/mousepad-util.h>
+#include <mousepad/mousepad-view.h>
+
+
+
+#define mousepad_view_get_buffer(view) (GTK_TEXT_VIEW (view)->buffer)
+
+
+
+static void mousepad_view_finalize (GObject *object);
+static void mousepad_view_style_set (GtkWidget *widget,
+ GtkStyle *previous_style);
+static gint mousepad_view_expose (GtkWidget *widget,
+ GdkEventExpose *event);
+static gboolean mousepad_view_key_press_event (GtkWidget *widget,
+ GdkEventKey *event);
+static gboolean mousepad_view_button_press_event (GtkWidget *widget,
+ GdkEventButton *event);
+static gboolean mousepad_view_button_release_event (GtkWidget *widget,
+ GdkEventButton *event);
+static gboolean mousepad_view_selection_word_range (const GtkTextIter *iter,
+ GtkTextIter *range_start,
+ GtkTextIter *range_end);
+static void mousepad_view_selection_key_press_event (MousepadView *view,
+ const gchar *text,
+ guint keyval,
+ guint modifiers);
+static void mousepad_view_commit_handler (GtkIMContext *context,
+ const gchar *str,
+ MousepadView *view);
+static void mousepad_view_selection_delete_content (MousepadView *view);
+static void mousepad_view_selection_destroy (MousepadView *view);
+static void mousepad_view_selection_draw (MousepadView *view,
+ gboolean append);
+static gboolean mousepad_view_selection_timeout (gpointer user_data);
+static void mousepad_view_selection_timeout_destroy (gpointer user_data);
+static gchar *mousepad_view_selection_string (MousepadView *view);
+static void mousepad_view_indent_increase (MousepadView *view,
+ GtkTextIter *iter);
+static void mousepad_view_indent_decrease (MousepadView *view,
+ GtkTextIter *iter);
+static void mousepad_view_indent_selection (MousepadView *view,
+ gboolean increase,
+ gboolean force);
+static void mousepad_view_transpose_multi_selection (GtkTextBuffer *buffer,
+ MousepadView *view);
+static void mousepad_view_transpose_range (GtkTextBuffer *buffer,
+ GtkTextIter *start_iter,
+ GtkTextIter *end_iter);
+static void mousepad_view_transpose_lines (GtkTextBuffer *buffer,
+ GtkTextIter *start_iter,
+ GtkTextIter *end_iter);
+static void mousepad_view_transpose_words (GtkTextBuffer *buffer,
+ GtkTextIter *iter);
+
+
+
+struct _MousepadViewClass
+{
+ GtkSourceViewClass __parent__;
+};
+
+struct _MousepadView
+{
+ GtkSourceView __parent__;
+
+ /* the selection style tag */
+ GtkTextTag *selection_tag;
+
+ /* list with all the selection marks */
+ GSList *selection_marks;
+
+ /* selection timeout id */
+ guint selection_timeout_id;
+
+ /* coordinates for the selection */
+ gint selection_start_x;
+ gint selection_start_y;
+ gint selection_end_x;
+ gint selection_end_y;
+
+ /* length of the selection (-1 = zerro width selection, 0 = no selection) */
+ gint selection_length;
+
+ /* if the selection is in editing mode */
+ guint selection_editing : 1;
+};
+
+
+
+G_DEFINE_TYPE (MousepadView, mousepad_view, GTK_TYPE_SOURCE_VIEW);
+
+
+
+static void
+mousepad_view_class_init (MousepadViewClass *klass)
+{
+ GObjectClass *gobject_class;
+ GtkWidgetClass *widget_class;
+
+ gobject_class = G_OBJECT_CLASS (klass);
+ gobject_class->finalize = mousepad_view_finalize;
+
+ widget_class = GTK_WIDGET_CLASS (klass);
+ widget_class->expose_event = mousepad_view_expose;
+ widget_class->style_set = mousepad_view_style_set;
+ widget_class->key_press_event = mousepad_view_key_press_event;
+ widget_class->button_press_event = mousepad_view_button_press_event;
+ widget_class->button_release_event = mousepad_view_button_release_event;
+}
+
+
+
+static void
+mousepad_view_init (MousepadView *view)
+{
+ /* initialize selection variables */
+ view->selection_timeout_id = 0;
+ view->selection_tag = NULL;
+ view->selection_marks = NULL;
+ view->selection_length = 0;
+ view->selection_editing = FALSE;
+
+ /* reset drag coordinates */
+ view->selection_start_x = view->selection_end_x = -1;
+ view->selection_start_y = view->selection_end_y = -1;
+
+ g_signal_connect (GTK_TEXT_VIEW (view)->im_context, "commit",
+ G_CALLBACK (mousepad_view_commit_handler), view);
+}
+
+
+
+static void
+mousepad_view_finalize (GObject *object)
+{
+ MousepadView *view = MOUSEPAD_VIEW (object);
+
+ /* stop a running selection timeout */
+ if (G_UNLIKELY (view->selection_timeout_id != 0))
+ g_source_remove (view->selection_timeout_id);
+
+ /* free the selection marks list (marks are owned by the buffer) */
+ if (G_UNLIKELY (view->selection_marks != NULL))
+ g_slist_free (view->selection_marks);
+
+ (*G_OBJECT_CLASS (mousepad_view_parent_class)->finalize) (object);
+}
+
+
+
+static gboolean
+mousepad_view_expose (GtkWidget *widget,
+ GdkEventExpose *event)
+{
+ GtkTextView *textview = GTK_TEXT_VIEW (widget);
+ MousepadView *view = MOUSEPAD_VIEW (widget);
+
+ if (G_UNLIKELY (view->selection_length == -1
+ && (view->selection_marks != NULL || view->selection_end_x != -1)
+ && event->window == gtk_text_view_get_window (textview, GTK_TEXT_WINDOW_TEXT)))
+ {
+ /* redraw the cursor lines for the vertical selection */
+ mousepad_view_selection_draw (view, FALSE);
+ }
+
+ /* gtk can draw the text now */
+ return (*GTK_WIDGET_CLASS (mousepad_view_parent_class)->expose_event) (widget, event);
+}
+
+
+
+static void
+mousepad_view_style_set (GtkWidget *widget,
+ GtkStyle *previous_style)
+{
+ GtkStyle *style;
+ GtkTextIter start, end;
+ MousepadView *view = MOUSEPAD_VIEW (widget);
+ GtkTextBuffer *buffer;
+
+ /* run widget handler */
+ (*GTK_WIDGET_CLASS (mousepad_view_parent_class)->style_set) (widget, previous_style);
+
+ /* update the style after a real style change */
+ if (previous_style)
+ {
+ /* get the textview style */
+ style = gtk_widget_get_style (widget);
+
+ /* get the text buffer */
+ buffer = mousepad_view_get_buffer (view);
+
+ /* remove previous selection tag */
+ if (G_UNLIKELY (view->selection_tag))
+ {
+ gtk_text_buffer_get_bounds (buffer, &start, &end);
+ gtk_text_buffer_remove_tag (buffer, view->selection_tag, &start, &end);
+ }
+
+ /* create the new selection tag */
+ view->selection_tag = gtk_text_buffer_create_tag (buffer, NULL,
+ "background-gdk", &style->base[GTK_STATE_SELECTED],
+ "foreground-gdk", &style->text[GTK_STATE_SELECTED],
+ NULL);
+
+ /* redraw selection */
+ if (view->selection_marks != NULL)
+ mousepad_view_selection_draw (view, FALSE);
+ }
+}
+
+
+
+static gboolean
+mousepad_view_key_press_event (GtkWidget *widget,
+ GdkEventKey *event)
+{
+ MousepadView *view = MOUSEPAD_VIEW (widget);
+ GtkTextBuffer *buffer;
+ GtkTextIter iter;
+ GtkTextMark *cursor;
+ guint modifiers;
+ gboolean im_handled;
+ gboolean is_editable;
+
+ /* get the modifiers state */
+ modifiers = event->state & gtk_accelerator_get_default_mod_mask ();
+
+ /* get the textview buffer */
+ buffer = mousepad_view_get_buffer (view);
+
+ /* whether the textview is editable */
+ is_editable = GTK_TEXT_VIEW (view)->editable;
+
+ /* handle the key event */
+ switch (event->keyval)
+ {
+ case GDK_End:
+ case GDK_KP_End:
+ if (modifiers & GDK_CONTROL_MASK)
+ {
+ /* get the end iter */
+ gtk_text_buffer_get_end_iter (buffer, &iter);
+
+ /* get the cursor mark */
+ cursor = gtk_text_buffer_get_insert (buffer);
+
+ goto move_cursor;
+ }
+ break;
+
+ case GDK_Home:
+ case GDK_KP_Home:
+ /* get the cursor mark */
+ cursor = gtk_text_buffer_get_insert (buffer);
+
+ /* when control is pressed, we jump to the start of the document */
+ if (modifiers & GDK_CONTROL_MASK)
+ {
+ /* get the start iter */
+ gtk_text_buffer_get_start_iter (buffer, &iter);
+
+ goto move_cursor;
+ }
+
+ /* get the iter position of the cursor */
+ gtk_text_buffer_get_iter_at_mark (buffer, &iter, cursor);
+
+ /* if the cursor starts a line, try to move it in front of the text */
+ if (gtk_text_iter_starts_line (&iter)
+ && mousepad_util_forward_iter_to_text (&iter, NULL))
+ {
+ /* label for the ctrl home/end events */
+ move_cursor:
+
+ /* move (select) or set (jump) cursor */
+ if (modifiers & GDK_SHIFT_MASK)
+ gtk_text_buffer_move_mark (buffer, cursor, &iter);
+ else
+ gtk_text_buffer_place_cursor (buffer, &iter);
+
+ /* make sure the cursor is visible for the user */
+ mousepad_view_scroll_to_cursor (view);
+
+ return TRUE;
+ }
+ break;
+
+ case GDK_Delete:
+ case GDK_KP_Delete:
+ if (view->selection_marks != NULL && is_editable)
+ {
+ /* delete or destroy the selection */
+ if (view->selection_length > 0)
+ mousepad_view_delete_selection (view);
+ else
+ mousepad_view_selection_destroy (view);
+
+ return TRUE;
+ }
+ break;
+
+ case GDK_BackSpace:
+ if (view->selection_marks != NULL && is_editable)
+ {
+ /* backspace in the selection */
+ mousepad_view_selection_key_press_event (view, NULL, GDK_BackSpace, modifiers);
+
+ return TRUE;
+ }
+ break;
+
+ default:
+ if (G_UNLIKELY (view->selection_marks != NULL && is_editable))
+ {
+ /* block textview updates (from gtk) */
+ gtk_text_view_set_editable (GTK_TEXT_VIEW (view), FALSE);
+
+ /* handle the im context */
+ im_handled = gtk_im_context_filter_keypress (GTK_TEXT_VIEW (view)->im_context, event);
+
+ /* allow textview updates again */
+ gtk_text_view_set_editable (GTK_TEXT_VIEW (view), TRUE);
+
+ /* only leave when the im handle the event */
+ if (im_handled)
+ return TRUE;
+ }
+ }
+
+ /* remove the selection when no valid key combination has been pressed */
+ if (G_UNLIKELY (view->selection_marks != NULL && event->is_modifier == FALSE))
+ mousepad_view_selection_destroy (view);
+
+ return (*GTK_WIDGET_CLASS (mousepad_view_parent_class)->key_press_event) (widget, event);
+}
+
+
+
+static void
+mousepad_view_commit_handler (GtkIMContext *context,
+ const gchar *str,
+ MousepadView *view)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_VIEW (view));
+ mousepad_return_if_fail (GTK_IS_IM_CONTEXT (context));
+
+ /* if there is a selection, insert this string there too */
+ if (G_UNLIKELY (view->selection_marks != NULL))
+ {
+ /* debug check */
+ mousepad_return_if_fail (gtk_text_view_get_editable (GTK_TEXT_VIEW (view)) == FALSE);
+
+ /* handle the text input for the multi selection */
+ mousepad_view_selection_key_press_event (view, str, 0, 0);
+ }
+}
+
+
+
+static gboolean
+mousepad_view_button_press_event (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ MousepadView *view = MOUSEPAD_VIEW (widget);
+ GtkTextView *textview = GTK_TEXT_VIEW (widget);
+ GtkTextIter iter, start_iter, end_iter;
+ GtkTextBuffer *buffer;
+
+ /* destroy old selection */
+ if (view->selection_marks != NULL && event->button != 3)
+ mousepad_view_selection_destroy (view);
+
+ /* work with vertical selection while ctrl is pressed */
+ if (event->state & GDK_CONTROL_MASK
+ && event->x >= 0
+ && event->y >= 0
+ && event->button == 1
+ && event->type == GDK_BUTTON_PRESS)
+ {
+ /* set the vertical selection start position, including textview offset */
+ view->selection_start_x = event->x + textview->xoffset;
+ view->selection_start_y = event->y + textview->yoffset;
+
+ /* hide cursor */
+ gtk_text_view_set_cursor_visible (textview, FALSE);
+
+ /* start a vertical selection timeout */
+ view->selection_timeout_id = g_timeout_add_full (G_PRIORITY_LOW, 50, mousepad_view_selection_timeout,
+ view, mousepad_view_selection_timeout_destroy);
+
+ return TRUE;
+ }
+ else if (event->type == GDK_2BUTTON_PRESS && event->button == 1)
+ {
+ /* get the iter under the cursor */
+ gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW (view), &iter,
+ event->x + textview->xoffset,
+ event->y + textview->yoffset);
+
+ /* try to select a whole word or space */
+ if (mousepad_view_selection_word_range (&iter, &start_iter, &end_iter))
+ {
+ /* get the buffer */
+ buffer = mousepad_view_get_buffer (view);
+
+ /* select range */
+ gtk_text_buffer_select_range (buffer, &end_iter, &start_iter);
+
+ return TRUE;
+ }
+ }
+
+ return (*GTK_WIDGET_CLASS (mousepad_view_parent_class)->button_press_event) (widget, event);
+}
+
+
+
+static gboolean
+mousepad_view_button_release_event (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ MousepadView *view = MOUSEPAD_VIEW (widget);
+
+ /* end of a vertical selection */
+ if (G_UNLIKELY (view->selection_timeout_id != 0))
+ {
+ /* stop the timeout */
+ g_source_remove (view->selection_timeout_id);
+
+ /* append the selected marks */
+ mousepad_view_selection_draw (view, TRUE);
+
+ /* reset the drag coordinates */
+ view->selection_start_x = view->selection_end_x = -1;
+ view->selection_start_y = view->selection_end_y = -1;
+ }
+
+ return (*GTK_WIDGET_CLASS (mousepad_view_parent_class)->button_release_event) (widget, event);
+}
+
+
+
+/**
+ * Selection Functions
+ **/
+static gboolean
+mousepad_view_selection_word_range (const GtkTextIter *iter,
+ GtkTextIter *range_start,
+ GtkTextIter *range_end)
+{
+ /* init iters */
+ *range_start = *range_end = *iter;
+
+ /* get the iter character */
+ if (!mousepad_util_iter_inside_word (iter))
+ {
+ /* walk back and forward to select as much spaces as possible */
+ if (!mousepad_util_iter_forward_text_start (range_end))
+ return FALSE;
+
+ if (!mousepad_util_iter_backward_text_start (range_start))
+ return FALSE;
+ }
+ else
+ {
+ /* return false when we could not find a word start */
+ if (!mousepad_util_iter_backward_word_start (range_start))
+ return FALSE;
+
+ /* return false when we could not find a word end */
+ if (!mousepad_util_iter_forward_word_end (range_end))
+ return FALSE;
+ }
+
+ /* sort iters */
+ gtk_text_iter_order (range_start, range_end);
+
+ /* when the iters are not equal and the origional iter is in the range, we succeeded */
+ return (!gtk_text_iter_equal (range_start, range_end)
+ && gtk_text_iter_compare (iter, range_start) >= 0
+ && gtk_text_iter_compare (iter, range_end) <= 0);
+}
+
+
+
+static void
+mousepad_view_selection_key_press_event (MousepadView *view,
+ const gchar *text,
+ guint keyval,
+ guint modifiers)
+{
+ GtkTextIter start_iter, end_iter;
+ GSList *li;
+ GtkTextBuffer *buffer;
+
+ mousepad_return_if_fail (view->selection_marks != NULL);
+ mousepad_return_if_fail (view->selection_start_x == -1);
+ mousepad_return_if_fail (view->selection_end_x == -1);
+ mousepad_return_if_fail ((keyval == 0 && text != NULL) || keyval != 0);
+ mousepad_return_if_fail (g_slist_length (view->selection_marks) % 2 == 0);
+
+ /* get the buffer */
+ buffer = mousepad_view_get_buffer (view);
+
+ /* begin user action */
+ gtk_text_buffer_begin_user_action (buffer);
+
+ /* delete the content of the selection when we're not in editing mode yet */
+ if (view->selection_editing == FALSE && view->selection_length > 0)
+ mousepad_view_selection_delete_content (view);
+
+ /* enter editing mode */
+ view->selection_editing = TRUE;
+
+ for (li = view->selection_marks; li != NULL; li = li->next)
+ {
+ /* get end iter */
+ gtk_text_buffer_get_iter_at_mark (buffer, &end_iter, li->next->data);
+
+ /* handle the event */
+ if (keyval == GDK_Tab)
+ {
+ /* insert a (soft) tab */
+ mousepad_view_indent_increase (view, &end_iter);
+ }
+ else if (keyval == GDK_BackSpace)
+ {
+ /* get start iter */
+ gtk_text_buffer_get_iter_at_mark (buffer, &start_iter, li->data);
+
+ /* only backspace when there is text inside the selection or ctrl is pressed */
+ if (!gtk_text_iter_equal (&start_iter, &end_iter) || modifiers == GDK_CONTROL_MASK)
+ gtk_text_buffer_backspace (buffer, &end_iter, FALSE, TRUE);
+ }
+ else if (text != NULL)
+ {
+ /* insert the text */
+ gtk_text_buffer_insert (buffer, &end_iter, text, -1);
+ }
+
+ /* go to next element in the list */
+ li = li->next;
+ }
+
+ /* redraw the content */
+ mousepad_view_selection_draw (view, FALSE);
+
+ /* end user action */
+ gtk_text_buffer_end_user_action (buffer);
+}
+
+
+
+static void
+mousepad_view_selection_delete_content (MousepadView *view)
+{
+ GtkTextBuffer *buffer;
+ GtkTextIter start_iter, end_iter;
+ GSList *li;
+
+ mousepad_return_if_fail (view->selection_marks != NULL);
+ mousepad_return_if_fail (view->selection_length > 0);
+ mousepad_return_if_fail (g_slist_length (view->selection_marks) % 2 == 0);
+
+ /* get the buffer */
+ buffer = mousepad_view_get_buffer (view);
+
+ /* begin user action */
+ gtk_text_buffer_begin_user_action (buffer);
+
+ /* remove everything between the marks */
+ for (li = view->selection_marks; li != NULL; li = li->next)
+ {
+ /* get start iter */
+ gtk_text_buffer_get_iter_at_mark (buffer, &start_iter, li->data);
+
+ /* next element */
+ li = li->next;
+
+ /* get end iter */
+ gtk_text_buffer_get_iter_at_mark (buffer, &end_iter, li->data);
+
+ /* delete content between the iters */
+ gtk_text_buffer_delete (buffer, &start_iter, &end_iter);
+ }
+
+ /* set the selection length to -1 */
+ view->selection_length = -1;
+
+ /* end user action */
+ gtk_text_buffer_end_user_action (buffer);
+}
+
+
+
+static void
+mousepad_view_selection_destroy (MousepadView *view)
+{
+ GtkTextBuffer *buffer;
+ GSList *li;
+ GtkTextIter start_iter, end_iter;
+
+ mousepad_return_if_fail (view->selection_marks != NULL);
+
+ /* get the buffer */
+ buffer = mousepad_view_get_buffer (view);
+
+ /* freeze notifications */
+ g_object_freeze_notify (G_OBJECT (buffer));
+
+ /* remove the selection tags */
+ gtk_text_buffer_get_bounds (buffer, &start_iter, &end_iter);
+ gtk_text_buffer_remove_tag (buffer, view->selection_tag, &start_iter, &end_iter);
+
+ /* remove all the selection marks */
+ if (G_LIKELY (view->selection_marks))
+ {
+ /* delete marks from the buffer */
+ for (li = view->selection_marks; li != NULL; li = li->next)
+ gtk_text_buffer_delete_mark (buffer, li->data);
+
+ /* free the list */
+ g_slist_free (view->selection_marks);
+
+ /* null */
+ view->selection_marks = NULL;
+ }
+
+ /* set selection length to zerro */
+ view->selection_length = 0;
+
+ /* unset editing mode */
+ view->selection_editing = FALSE;
+
+ /* show cursor again */
+ gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (view), TRUE);
+
+ /* update buffer status */
+ g_object_notify (G_OBJECT (buffer), "has-selection");
+
+ /* allow notifications again */
+ g_object_thaw_notify (G_OBJECT (buffer));
+}
+
+
+
+static void
+mousepad_view_selection_draw (MousepadView *view,
+ gboolean append)
+{
+ GtkTextBuffer *buffer;
+ GtkTextView *textview = GTK_TEXT_VIEW (view);
+ GtkTextIter start_iter, end_iter;
+ GdkWindow *window;
+ gint y, y_begin, y_height, y_end;
+ gint line_x, line_y;
+ GdkRectangle rect;
+ gint length = 0;
+ gint visible_start_y = 0;
+ GtkTextMark *start_mark, *end_mark;
+ GSList *li;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_VIEW (view));
+ mousepad_return_if_fail (view->selection_marks == NULL || g_slist_length (view->selection_marks) % 2 == 0);
+
+ /* get the buffer */
+ buffer = mousepad_view_get_buffer (view);
+
+ /* freeze buffer notifications */
+ g_object_freeze_notify (G_OBJECT (buffer));
+
+ /* get the drawable window */
+ window = gtk_text_view_get_window (textview, GTK_TEXT_WINDOW_TEXT);
+
+ /* cleanup the old selection */
+ if (view->selection_length > 0)
+ {
+ /* remove the old tags */
+ gtk_text_buffer_get_bounds (buffer, &start_iter, &end_iter);
+ gtk_text_buffer_remove_tag (buffer, view->selection_tag, &start_iter, &end_iter);
+ }
+ else if (view->selection_length == -1)
+ {
+ /* invalidate the window so no retangles are left */
+ gtk_widget_queue_draw (GTK_WIDGET (view));
+ }
+
+ /* draw the dragging selection or the marks in the list */
+ if (view->selection_start_y != -1 && view->selection_end_y != -1)
+ {
+ /* get the start and end iter */
+ gtk_text_view_get_iter_at_location (textview, &start_iter, 0, view->selection_start_y);
+ gtk_text_view_get_iter_at_location (textview, &end_iter, 0, view->selection_end_y);
+
+ /* make sure the start iter is before the end iter */
+ gtk_text_iter_order (&start_iter, &end_iter);
+
+ /* get the y coordinates we're going to walk */
+ gtk_text_view_get_line_yrange (textview, &start_iter, &y_begin, &y_height);
+ gtk_text_view_get_line_yrange (textview, &end_iter, &y_end, NULL);
+
+ /* make sure atleast one line is selected */
+ y_end += y_height;
+
+ /* get the visible area */
+ if (G_LIKELY (append == FALSE))
+ {
+ visible_start_y = MIN (y_begin, y_end);
+ visible_start_y = MAX (visible_start_y, textview->yoffset);
+ }
+
+ /* walk the lines inside the selection area */
+ for (y = y_begin; y < y_end; y += y_height)
+ {
+ /* get the start and end iter in the line */
+ gtk_text_view_get_iter_at_location (textview, &start_iter, view->selection_start_x, y);
+ gtk_text_view_get_iter_at_location (textview, &end_iter, view->selection_end_x, y);
+
+ /* make sure the iters are correctly sorted */
+ gtk_text_iter_order (&start_iter, &end_iter);
+
+ /* calculate length of selection */
+ length += gtk_text_iter_get_line_offset (&end_iter) - gtk_text_iter_get_line_offset (&start_iter);
+
+ /* continue if this iter is outside the visible area */
+ if (y < visible_start_y)
+ continue;
+
+ if (length == 0)
+ {
+ /* get the iter location and size */
+ gtk_text_view_get_iter_location (textview, &end_iter, &rect);
+
+ /* calculate line cooridinates */
+ line_x = rect.x - textview->xoffset;
+ line_y = rect.y - textview->yoffset;
+
+ /* draw a line in front of the iter */
+ gdk_draw_line (GDK_DRAWABLE (window),
+ GTK_WIDGET (view)->style->base_gc[GTK_STATE_SELECTED],
+ line_x, line_y, line_x, line_y + rect.height - 1);
+ }
+ else if (!gtk_text_iter_equal (&start_iter, &end_iter))
+ {
+ /* apply tag */
+ gtk_text_buffer_apply_tag (buffer, view->selection_tag, &start_iter, &end_iter);
+ }
+ else
+ {
+ /* when both of the conditions above were not met, we'll
+ * never added them to the marks list and there is nothing to draw
+ * eighter */
+ continue;
+ }
+
+ if (append)
+ {
+ /* create marks */
+ start_mark = gtk_text_buffer_create_mark (buffer, NULL, &start_iter, TRUE);
+ end_mark = gtk_text_buffer_create_mark (buffer, NULL, &end_iter, FALSE);
+
+ /* append to the list */
+ view->selection_marks = g_slist_append (view->selection_marks, start_mark);
+ view->selection_marks = g_slist_append (view->selection_marks, end_mark);
+ }
+ }
+ }
+ else
+ {
+ for (li = view->selection_marks; li != NULL; li = li->next)
+ {
+ /* get the start iter */
+ gtk_text_buffer_get_iter_at_mark (buffer, &start_iter, li->data);
+
+ /* next element in the list */
+ li = li->next;
+
+ /* get the end iter */
+ gtk_text_buffer_get_iter_at_mark (buffer, &end_iter, li->data);
+
+ /* calculate length of selection */
+ length += gtk_text_iter_get_line_offset (&end_iter) - gtk_text_iter_get_line_offset (&start_iter);
+
+ if (length == 0)
+ {
+ /* get the iter location and size */
+ gtk_text_view_get_iter_location (textview, &start_iter, &rect);
+
+ /* calculate line coordinates */
+ line_x = rect.x - textview->xoffset;
+ line_y = rect.y - textview->yoffset;
+
+ /* draw a line in front of the iter */
+ gdk_draw_line (GDK_DRAWABLE (window),
+ GTK_WIDGET (view)->style->base_gc[GTK_STATE_SELECTED],
+ line_x, line_y, line_x, line_y + rect.height - 1);
+ }
+ else
+ {
+ /* apply tag */
+ gtk_text_buffer_apply_tag (buffer, view->selection_tag, &start_iter, &end_iter);
+ }
+ }
+ }
+
+ /* emit signal for selection (type) change */
+ if ((length == 0 && view->selection_length != -1)
+ || (length > 0 && view->selection_length <= 0))
+ g_object_notify (G_OBJECT (buffer), "has-selection");
+
+ /* update the selection length */
+ view->selection_length = length > 0 ? length : -1;
+
+ /* allow sending notifications again */
+ g_object_thaw_notify (G_OBJECT (buffer));
+}
+
+
+
+static gboolean
+mousepad_view_selection_timeout (gpointer user_data)
+{
+ MousepadView *view = MOUSEPAD_VIEW (user_data);
+ GtkTextView *textview = GTK_TEXT_VIEW (view);
+ gint pointer_x, pointer_y;
+ gint selection_start_x, selection_start_y;
+ GtkTextBuffer *buffer;
+ GtkTextIter iter, cursor;
+
+ GDK_THREADS_ENTER ();
+
+ /* get the cursor coordinate */
+ gdk_window_get_pointer (gtk_text_view_get_window (textview, GTK_TEXT_WINDOW_TEXT),
+ &pointer_x, &pointer_y, NULL);
+
+ /* convert to positive values and add buffer offset */
+ pointer_x = MAX (pointer_x, 0) + textview->xoffset;
+ pointer_y = MAX (pointer_y, 0) + textview->yoffset;
+
+ /* only update the selection when the cursor moved */
+ if (view->selection_end_x != pointer_x || view->selection_end_y != pointer_y)
+ {
+ /* update the end coordinates */
+ view->selection_end_x = pointer_x;
+ view->selection_end_y = pointer_y;
+
+ /* backup start coordinates */
+ selection_start_x = view->selection_start_x;
+ selection_start_y = view->selection_start_y;
+
+ /* get the text buffer */
+ buffer = mousepad_view_get_buffer (view);
+
+ /* get the iter position of the cursor based on the coordinates */
+ gtk_text_view_get_iter_at_location (textview, &iter, pointer_x, pointer_y);
+
+ /* get the real cursor position */
+ gtk_text_buffer_get_iter_at_mark (buffer, &cursor, gtk_text_buffer_get_insert (buffer));
+
+ /* redraw the selection when the cursor position changed */
+ if (!gtk_text_iter_equal (&iter, &cursor))
+ {
+ /* show the selection */
+ mousepad_view_selection_draw (view, FALSE);
+
+ /* update the cursor position */
+ gtk_text_buffer_place_cursor (buffer, &iter);
+
+ /* put cursor on screen */
+ mousepad_view_scroll_to_cursor (view);
+ }
+ }
+
+ GDK_THREADS_LEAVE ();
+
+ /* keep the timeout running */
+ return TRUE;
+}
+
+
+
+static void
+mousepad_view_selection_timeout_destroy (gpointer user_data)
+{
+ MOUSEPAD_VIEW (user_data)->selection_timeout_id = 0;
+}
+
+
+
+static gchar *
+mousepad_view_selection_string (MousepadView *view)
+{
+ GString *string;
+ GtkTextBuffer *buffer;
+ gint line, previous_line = -1;
+ gchar *slice;
+ GtkTextIter start_iter, end_iter;
+ GSList *li;
+
+ mousepad_return_val_if_fail (view->selection_marks == NULL || g_slist_length (view->selection_marks) % 2 == 0, NULL);
+
+ /* create new string */
+ string = g_string_new (NULL);
+
+ /* get the buffer */
+ buffer = mousepad_view_get_buffer (view);
+
+ for (li = view->selection_marks; li != NULL; li = li->next)
+ {
+ /* get the iters */
+ gtk_text_buffer_get_iter_at_mark (buffer, &start_iter, li->data);
+ li = li->next;
+ gtk_text_buffer_get_iter_at_mark (buffer, &end_iter, li->data);
+
+ /* get the line number */
+ line = gtk_text_iter_get_line (&start_iter);
+
+ /* fix the number of new lines between the parts */
+ if (previous_line == -1)
+ previous_line = line;
+ else
+ for (; previous_line < line; previous_line++)
+ string = g_string_append_c (string, '\n');
+
+ /* get the text slice */
+ slice = gtk_text_buffer_get_slice (buffer, &start_iter, &end_iter, TRUE);
+
+ /* append the slice to the string */
+ string = g_string_append (string, slice);
+
+ /* cleanup */
+ g_free (slice);
+ }
+
+ /* return the string */
+ return g_string_free (string, FALSE);
+}
+
+
+
+/**
+ * Indentation Functions
+ **/
+static void
+mousepad_view_indent_increase (MousepadView *view,
+ GtkTextIter *iter)
+{
+ gchar *string;
+ gint offset, length, inline_len, tab_size;
+ GtkTextBuffer *buffer;
+
+ /* get the buffer */
+ buffer = mousepad_view_get_buffer (view);
+ tab_size = gtk_source_view_get_tab_width (GTK_SOURCE_VIEW (view));
+
+ if (gtk_source_view_get_insert_spaces_instead_of_tabs (GTK_SOURCE_VIEW (view)))
+ {
+ /* get the offset */
+ offset = mousepad_util_get_real_line_offset (iter, tab_size);
+
+ /* calculate the length to inline with a tab */
+ inline_len = offset % tab_size;
+
+ if (inline_len == 0)
+ length = tab_size;
+ else
+ length = tab_size - inline_len;
+
+ /* create spaces string */
+ string = g_strnfill (length, ' ');
+
+ /* insert string */
+ gtk_text_buffer_insert (buffer, iter, string, length);
+
+ /* cleanup */
+ g_free (string);
+ }
+ else
+ {
+ /* insert a tab or a space */
+ gtk_text_buffer_insert (buffer, iter, "\t", -1);
+ }
+}
+
+
+
+static void
+mousepad_view_indent_decrease (MousepadView *view,
+ GtkTextIter *iter)
+{
+ GtkTextBuffer *buffer;
+ GtkTextIter start, end;
+ gint columns, tab_size;
+ gunichar c;
+
+ /* set iters */
+ start = end = *iter;
+
+ tab_size = gtk_source_view_get_tab_width (GTK_SOURCE_VIEW (view));
+ columns = tab_size;
+
+ /* walk until we've removed enough columns */
+ while (columns > 0)
+ {
+ /* get the character */
+ c = gtk_text_iter_get_char (&end);
+
+ if (c == '\t')
+ columns -= tab_size;
+ else if (c == ' ')
+ columns--;
+ else
+ break;
+
+ /* go to the next character */
+ gtk_text_iter_forward_char (&end);
+ }
+
+ /* unindent the selection when the iters are not equal */
+ if (!gtk_text_iter_equal (&start, &end))
+ {
+ /* get the buffer */
+ buffer = mousepad_view_get_buffer (view);
+
+ /* remove the columns */
+ gtk_text_buffer_delete (buffer, &start, &end);
+ }
+}
+
+
+
+static void
+mousepad_view_indent_selection (MousepadView *view,
+ gboolean increase,
+ gboolean force)
+{
+ GtkTextBuffer *buffer;
+ GtkTextIter start_iter, end_iter;
+ gint start_line, end_line;
+ gint i;
+
+ /* get the textview buffer */
+ buffer = mousepad_view_get_buffer (view);
+
+ if (gtk_text_buffer_get_selection_bounds (buffer, &start_iter, &end_iter) || force)
+ {
+ /* begin a user action */
+ gtk_text_buffer_begin_user_action (buffer);
+
+ /* get start and end line */
+ start_line = gtk_text_iter_get_line (&start_iter);
+ end_line = gtk_text_iter_get_line (&end_iter);
+
+ /* only change indentation when an entire line is selected or multiple lines */
+ if (start_line != end_line
+ || ((gtk_text_iter_starts_line (&start_iter) && gtk_text_iter_ends_line (&end_iter)) || force))
+ {
+ /* change indentation of each line */
+ for (i = start_line; i <= end_line && i < G_MAXINT; i++)
+ {
+ /* get the iter of the line we're going to indent */
+ gtk_text_buffer_get_iter_at_line (buffer, &start_iter, i);
+
+ /* don't change indentation of empty lines */
+ if (gtk_text_iter_ends_line (&start_iter))
+ continue;
+
+ /* increase or decrease the indentation */
+ if (increase)
+ mousepad_view_indent_increase (view, &start_iter);
+ else
+ mousepad_view_indent_decrease (view, &start_iter);
+ }
+ }
+
+ /* end user action */
+ gtk_text_buffer_end_user_action (buffer);
+
+ /* put cursor on screen */
+ mousepad_view_scroll_to_cursor (view);
+ }
+}
+
+
+void
+mousepad_view_scroll_to_cursor (MousepadView *view)
+{
+ GtkTextBuffer *buffer;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_VIEW (view));
+
+ /* get the buffer */
+ buffer = mousepad_view_get_buffer (view);
+
+ /* scroll to visible area */
+ gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (view),
+ gtk_text_buffer_get_insert (buffer),
+ 0.02, FALSE, 0.0, 0.0);
+}
+
+
+
+static void
+mousepad_view_transpose_multi_selection (GtkTextBuffer *buffer,
+ MousepadView *view)
+{
+ GSList *li, *ls;
+ GSList *strings = NULL;
+ GtkTextIter start_iter, end_iter;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_VIEW (view));
+
+ /* get the strings and delete the existing strings */
+ for (li = view->selection_marks; li != NULL; li = li->next)
+ {
+ /* get the iters */
+ gtk_text_buffer_get_iter_at_mark (buffer, &start_iter, li->data);
+ li = li->next;
+ gtk_text_buffer_get_iter_at_mark (buffer, &end_iter, li->data);
+
+ /* prepend the text between the iters to an internal list */
+ strings = g_slist_prepend (strings, gtk_text_buffer_get_slice (buffer, &start_iter, &end_iter, FALSE));
+
+ /* delete the content */
+ gtk_text_buffer_delete (buffer, &start_iter, &end_iter);
+ }
+
+ /* insert the strings in reversed order */
+ for (li = view->selection_marks, ls = strings; li != NULL && ls != NULL; li = li->next, ls = ls->next)
+ {
+ /* get the end iter */
+ gtk_text_buffer_get_iter_at_mark (buffer, &end_iter, li->next->data);
+
+ /* insert the string */
+ gtk_text_buffer_insert (buffer, &end_iter, ls->data, -1);
+
+ /* get the start iter */
+ gtk_text_buffer_get_iter_at_mark (buffer, &start_iter, li->data);
+
+ /* apply tag */
+ gtk_text_buffer_apply_tag (buffer, view->selection_tag, &start_iter, &end_iter);
+
+ /* free string */
+ g_free (ls->data);
+
+ /* next iter */
+ li = li->next;
+ }
+
+ /* free list */
+ g_slist_free (strings);
+}
+
+
+
+static void
+mousepad_view_transpose_range (GtkTextBuffer *buffer,
+ GtkTextIter *start_iter,
+ GtkTextIter *end_iter)
+{
+ gchar *string, *reversed;
+ gint offset;
+
+ /* store start iter line offset */
+ offset = gtk_text_iter_get_offset (start_iter);
+
+ /* get selected text */
+ string = gtk_text_buffer_get_slice (buffer, start_iter, end_iter, FALSE);
+ if (G_LIKELY (string))
+ {
+ /* reverse the string */
+ reversed = g_utf8_strreverse (string, -1);
+
+ /* only change the buffer then the string changed */
+ if (G_LIKELY (reversed && strcmp (reversed, string) != 0))
+ {
+ /* delete the text between the iters */
+ gtk_text_buffer_delete (buffer, start_iter, end_iter);
+
+ /* insert the reversed string */
+ gtk_text_buffer_insert (buffer, end_iter, reversed, -1);
+
+ /* restore start iter */
+ gtk_text_buffer_get_iter_at_offset (buffer, start_iter, offset);
+ }
+
+ /* cleanup */
+ g_free (string);
+ g_free (reversed);
+ }
+}
+
+
+
+static void
+mousepad_view_transpose_lines (GtkTextBuffer *buffer,
+ GtkTextIter *start_iter,
+ GtkTextIter *end_iter)
+{
+ GString *string;
+ gint start_line, end_line;
+ gint i;
+ gchar *slice;
+
+ /* make sure the order is ok */
+ gtk_text_iter_order (start_iter, end_iter);
+
+ /* get the line numbers */
+ start_line = gtk_text_iter_get_line (start_iter);
+ end_line = gtk_text_iter_get_line (end_iter);
+
+ /* new string */
+ string = g_string_new (NULL);
+
+ /* add the lines in reversed order to the string */
+ for (i = start_line; i <= end_line && i < G_MAXINT; i++)
+ {
+ /* get start iter */
+ gtk_text_buffer_get_iter_at_line (buffer, start_iter, i);
+
+ /* set end iter */
+ *end_iter = *start_iter;
+
+ /* only prepend when the iters won't be equal */
+ if (!gtk_text_iter_ends_line (end_iter))
+ {
+ /* move the iter to the end of this line */
+ gtk_text_iter_forward_to_line_end (end_iter);
+
+ /* prepend line */
+ slice = gtk_text_buffer_get_slice (buffer, start_iter, end_iter, FALSE);
+ string = g_string_prepend (string, slice);
+ g_free (slice);
+ }
+
+ /* prepend new line */
+ if (i < end_line)
+ string = g_string_prepend_c (string, '\n');
+ }
+
+ /* get start iter again */
+ gtk_text_buffer_get_iter_at_line (buffer, start_iter, start_line);
+
+ /* delete selection */
+ gtk_text_buffer_delete (buffer, start_iter, end_iter);
+
+ /* insert reversed lines */
+ gtk_text_buffer_insert (buffer, end_iter, string->str, string->len);
+
+ /* cleanup */
+ g_string_free (string, TRUE);
+
+ /* restore start iter */
+ gtk_text_buffer_get_iter_at_line (buffer, start_iter, start_line);
+}
+
+static void
+mousepad_view_transpose_words (GtkTextBuffer *buffer,
+ GtkTextIter *iter)
+{
+ GtkTextMark *left_mark, *right_mark;
+ GtkTextIter start_left, end_left, start_right, end_right;
+ gchar *word_left, *word_right;
+ gboolean restore_cursor;
+
+ /* left word end */
+ end_left = *iter;
+ if (!mousepad_util_iter_backward_text_start (&end_left))
+ return;
+
+ /* left word start */
+ start_left = end_left;
+ if (!mousepad_util_iter_backward_word_start (&start_left))
+ return;
+
+ /* right word start */
+ start_right = *iter;
+ if (!mousepad_util_iter_forward_text_start (&start_right))
+ return;
+
+ /* right word end */
+ end_right = start_right;
+ if (!mousepad_util_iter_forward_word_end (&end_right))
+ return;
+
+ /* check if we selected something usefull */
+ if (gtk_text_iter_get_line (&start_left) == gtk_text_iter_get_line (&end_right)
+ && !gtk_text_iter_equal (&start_left, &end_left)
+ && !gtk_text_iter_equal (&start_right, &end_right)
+ && !gtk_text_iter_equal (&end_left, &start_right))
+ {
+ /* get the words */
+ word_left = gtk_text_buffer_get_slice (buffer, &start_left, &end_left, FALSE);
+ word_right = gtk_text_buffer_get_slice (buffer, &start_right, &end_right, FALSE);
+
+ /* check if we need to restore the cursor afterwards */
+ restore_cursor = gtk_text_iter_equal (iter, &start_right);
+
+ /* insert marks at the start and end of the right word */
+ left_mark = gtk_text_buffer_create_mark (buffer, NULL, &start_right, TRUE);
+ right_mark = gtk_text_buffer_create_mark (buffer, NULL, &end_right, FALSE);
+
+ /* delete the left word and insert the right word there */
+ gtk_text_buffer_delete (buffer, &start_left, &end_left);
+ gtk_text_buffer_insert (buffer, &start_left, word_right, -1);
+ g_free (word_right);
+
+ /* restore the right word iters */
+ gtk_text_buffer_get_iter_at_mark (buffer, &start_right, left_mark);
+ gtk_text_buffer_get_iter_at_mark (buffer, &end_right, right_mark);
+
+ /* delete the right words and insert the left word there */
+ gtk_text_buffer_delete (buffer, &start_right, &end_right);
+ gtk_text_buffer_insert (buffer, &end_right, word_left, -1);
+ g_free (word_left);
+
+ /* restore the cursor if needed */
+ if (restore_cursor)
+ {
+ /* restore the iter from the left mark (left gravity) */
+ gtk_text_buffer_get_iter_at_mark (buffer, &start_right, left_mark);
+ gtk_text_buffer_place_cursor (buffer, &start_right);
+ }
+
+ /* cleanup the marks */
+ gtk_text_buffer_delete_mark (buffer, left_mark);
+ gtk_text_buffer_delete_mark (buffer, right_mark);
+ }
+}
+
+
+
+void
+mousepad_view_transpose (MousepadView *view)
+{
+ GtkTextBuffer *buffer;
+ GtkTextIter sel_start, sel_end;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_VIEW (view));
+
+ /* get the buffer */
+ buffer = mousepad_view_get_buffer (view);
+
+ /* begin user action */
+ gtk_text_buffer_begin_user_action (buffer);
+
+ if (view->selection_marks != NULL)
+ {
+ /* transpose a multi selection */
+ mousepad_view_transpose_multi_selection (buffer, view);
+ }
+ else if (gtk_text_buffer_get_selection_bounds (buffer, &sel_start, &sel_end))
+ {
+ /* if the selection is not on the same line, include the whole lines */
+ if (gtk_text_iter_get_line (&sel_start) == gtk_text_iter_get_line (&sel_end))
+ {
+ /* reverse selection */
+ mousepad_view_transpose_range (buffer, &sel_start, &sel_end);
+ }
+ else
+ {
+ /* reverse lines */
+ mousepad_view_transpose_lines (buffer, &sel_start, &sel_end);
+ }
+
+ /* restore selection */
+ gtk_text_buffer_select_range (buffer, &sel_end, &sel_start);
+ }
+ else
+ {
+ /* get cursor iter */
+ gtk_text_buffer_get_iter_at_mark (buffer, &sel_start, gtk_text_buffer_get_insert (buffer));
+
+ /* set end iter */
+ sel_end = sel_start;
+
+ if (gtk_text_iter_starts_line (&sel_start))
+ {
+ /* swap this line with the line above */
+ if (gtk_text_iter_backward_line (&sel_end))
+ mousepad_view_transpose_lines (buffer, &sel_start, &sel_end);
+ }
+ else if (gtk_text_iter_ends_line (&sel_start))
+ {
+ /* swap this line with the line below */
+ if (gtk_text_iter_forward_line (&sel_end))
+ mousepad_view_transpose_lines (buffer, &sel_start, &sel_end);
+ }
+ else if (mousepad_util_iter_inside_word (&sel_start))
+ {
+ /* reverse the characters before and after the cursor */
+ if (gtk_text_iter_backward_char (&sel_start) && gtk_text_iter_forward_char (&sel_end))
+ mousepad_view_transpose_range (buffer, &sel_start, &sel_end);
+ }
+ else
+ {
+ /* swap the words left and right of the cursor */
+ mousepad_view_transpose_words (buffer, &sel_start);
+ }
+ }
+
+ /* end user action */
+ gtk_text_buffer_end_user_action (buffer);
+}
+
+
+
+void
+mousepad_view_clipboard_cut (MousepadView *view)
+{
+ GtkClipboard *clipboard;
+ GtkTextBuffer *buffer;
+ gchar *string;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_VIEW (view));
+ mousepad_return_if_fail (mousepad_view_get_selection_length (view, NULL) > 0);
+
+ /* get the clipboard */
+ clipboard = gtk_widget_get_clipboard (GTK_WIDGET (view), GDK_SELECTION_CLIPBOARD);
+
+ if (view->selection_marks != NULL)
+ {
+ /* get the selection string */
+ string = mousepad_view_selection_string (view);
+
+ /* set the clipboard text */
+ gtk_clipboard_set_text (clipboard, string, -1);
+
+ /* cleanup */
+ g_free (string);
+
+ /* cleanup the vertical selection */
+ mousepad_view_selection_delete_content (view);
+
+ /* destroy selection */
+ mousepad_view_selection_destroy (view);
+ }
+ else
+ {
+ /* get the buffer */
+ buffer = mousepad_view_get_buffer (view);
+
+ /* cut from buffer */
+ gtk_text_buffer_cut_clipboard (buffer, clipboard, gtk_text_view_get_editable (GTK_TEXT_VIEW (view)));
+ }
+
+ /* put cursor on screen */
+ mousepad_view_scroll_to_cursor (view);
+}
+
+
+
+void
+mousepad_view_clipboard_copy (MousepadView *view)
+{
+ GtkClipboard *clipboard;
+ GtkTextBuffer *buffer;
+ gchar *string;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_VIEW (view));
+ mousepad_return_if_fail (mousepad_view_get_selection_length (view, NULL) > 0);
+
+ /* get the clipboard */
+ clipboard = gtk_widget_get_clipboard (GTK_WIDGET (view), GDK_SELECTION_CLIPBOARD);
+
+ if (view->selection_marks != NULL)
+ {
+ /* get the selection string */
+ string = mousepad_view_selection_string (view);
+
+ /* set the clipboard text */
+ gtk_clipboard_set_text (clipboard, string, -1);
+
+ /* cleanup */
+ g_free (string);
+ }
+ else
+ {
+ /* get the buffer */
+ buffer = mousepad_view_get_buffer (view);
+
+ /* copy from buffer */
+ gtk_text_buffer_copy_clipboard (buffer, clipboard);
+ }
+
+ /* put cursor on screen */
+ mousepad_view_scroll_to_cursor (view);
+}
+
+
+
+void
+mousepad_view_clipboard_paste (MousepadView *view,
+ const gchar *string,
+ gboolean paste_as_column)
+{
+ GtkClipboard *clipboard;
+ GtkTextBuffer *buffer;
+ GtkTextView *textview = GTK_TEXT_VIEW (view);
+ gchar *text = NULL;
+ GtkTextMark *mark;
+ GtkTextIter iter;
+ GtkTextIter start_iter, end_iter;
+ GdkRectangle rect;
+ gchar **pieces;
+ gint i, y;
+
+ if (string == NULL)
+ {
+ /* get the clipboard */
+ clipboard = gtk_widget_get_clipboard (GTK_WIDGET (view), GDK_SELECTION_CLIPBOARD);
+
+ /* get the clipboard text */
+ text = gtk_clipboard_wait_for_text (clipboard);
+
+ /* leave when the text is null */
+ if (G_UNLIKELY (text == NULL))
+ return;
+
+ /* set the string */
+ string = text;
+ }
+
+ /* get the buffer */
+ buffer = mousepad_view_get_buffer (view);
+
+ /* begin user action */
+ gtk_text_buffer_begin_user_action (buffer);
+
+ if (paste_as_column)
+ {
+ /* chop the string into pieces */
+ pieces = g_strsplit (string, "\n", -1);
+
+ /* get iter at cursor position */
+ mark = gtk_text_buffer_get_insert (buffer);
+ gtk_text_buffer_get_iter_at_mark (buffer, &iter, mark);
+
+ /* get the iter location */
+ gtk_text_view_get_iter_location (textview, &iter, &rect);
+
+ /* insert the pieces in the buffer */
+ for (i = 0; pieces[i] != NULL; i++)
+ {
+ /* insert the text in the buffer */
+ gtk_text_buffer_insert (buffer, &iter, pieces[i], -1);
+
+ /* break if the next piece is null */
+ if (G_UNLIKELY (pieces[i+1] == NULL))
+ break;
+
+ /* move the iter to the next line */
+ if (!gtk_text_iter_forward_line (&iter))
+ {
+ /* no new line, insert a new line */
+ gtk_text_buffer_insert (buffer, &iter, "\n", 1);
+ }
+ else
+ {
+ /* get the y coordinate for this line */
+ gtk_text_view_get_line_yrange (textview, &iter, &y, NULL);
+
+ /* get the iter at the correct coordinate */
+ gtk_text_view_get_iter_at_location (textview, &iter, rect.x, y);
+ }
+ }
+
+ /* cleanup */
+ g_strfreev (pieces);
+
+ /* set the cursor to the last iter position */
+ gtk_text_buffer_place_cursor (buffer, &iter);
+ }
+ else
+ {
+ /* get selection bounds */
+ gtk_text_buffer_get_selection_bounds (buffer, &start_iter, &end_iter);
+
+ /* remove the existing selection if the iters are not equal */
+ if (!gtk_text_iter_equal (&start_iter, &end_iter))
+ gtk_text_buffer_delete (buffer, &start_iter, &end_iter);
+
+ /* insert string */
+ gtk_text_buffer_insert (buffer, &start_iter, string, -1);
+ }
+
+ /* cleanup */
+ g_free (text);
+
+ /* end user action */
+ gtk_text_buffer_end_user_action (buffer);
+
+ /* put cursor on screen */
+ mousepad_view_scroll_to_cursor (view);
+}
+
+
+
+void
+mousepad_view_delete_selection (MousepadView *view)
+{
+ GtkTextBuffer *buffer;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_VIEW (view));
+ mousepad_return_if_fail (mousepad_view_get_selection_length (view, NULL) > 0);
+
+ if (view->selection_marks != NULL)
+ {
+ /* remove the text in our selection */
+ mousepad_view_selection_delete_content (view);
+
+ /* cleanup the vertical selection */
+ mousepad_view_selection_destroy (view);
+ }
+ else
+ {
+ /* get the buffer */
+ buffer = mousepad_view_get_buffer (view);
+
+ /* delete the selection */
+ gtk_text_buffer_delete_selection (buffer, TRUE, gtk_text_view_get_editable (GTK_TEXT_VIEW (view)));
+ }
+
+ /* put cursor on screen */
+ mousepad_view_scroll_to_cursor (view);
+}
+
+
+
+void
+mousepad_view_select_all (MousepadView *view)
+{
+ GtkTextIter start, end;
+ GtkTextBuffer *buffer;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_VIEW (view));
+
+ /* cleanup our selection */
+ if (view->selection_marks != NULL)
+ mousepad_view_selection_destroy (view);
+
+ /* get the buffer */
+ buffer = mousepad_view_get_buffer (view);
+
+ /* get the start and end iter */
+ gtk_text_buffer_get_bounds (buffer, &start, &end);
+
+ /* select everything between those iters */
+ gtk_text_buffer_select_range (buffer, &end, &start);
+}
+
+
+
+void
+mousepad_view_change_selection (MousepadView *view)
+{
+ GtkTextBuffer *buffer;
+ GtkTextIter start_iter, end_iter;
+ GdkRectangle rect;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_VIEW (view));
+ mousepad_return_if_fail (mousepad_view_get_selection_length (view, NULL) != 0);
+
+ /* get the buffer */
+ buffer = mousepad_view_get_buffer (view);
+
+ /* freeze notifications */
+ g_object_freeze_notify (G_OBJECT (buffer));
+
+ if (view->selection_marks != NULL)
+ {
+ /* get the first and last iter from the selection */
+ gtk_text_buffer_get_iter_at_mark (buffer, &start_iter, view->selection_marks->data);
+ gtk_text_buffer_get_iter_at_mark (buffer, &end_iter, g_slist_last (view->selection_marks)->data);
+
+ /* sort the iters */
+ gtk_text_iter_order (&start_iter, &end_iter);
+
+ /* destroy the selection */
+ mousepad_view_selection_destroy (view);
+
+ /* select the range */
+ gtk_text_buffer_select_range (buffer, &end_iter, &start_iter);
+ }
+ else if (gtk_text_buffer_get_selection_bounds (buffer, &start_iter, &end_iter))
+ {
+ /* set the start coordinates */
+ gtk_text_view_get_iter_location (GTK_TEXT_VIEW (view), &start_iter, &rect);
+ view->selection_start_x = rect.x;
+ view->selection_start_y = rect.y;
+
+ /* set the end coordinates */
+ gtk_text_view_get_iter_location (GTK_TEXT_VIEW (view), &end_iter, &rect);
+ view->selection_end_x = rect.x;
+ view->selection_end_y = rect.y;
+
+ /* drop the normal selection and hide cursor */
+ gtk_text_buffer_place_cursor (buffer, &end_iter);
+ gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (view), FALSE);
+
+ /* poke selection function to add the marks */
+ mousepad_view_selection_draw (view, TRUE);
+
+ /* reset the coordinates */
+ view->selection_start_x = view->selection_start_y = -1;
+ view->selection_end_x = view->selection_end_y = -1;
+ }
+
+ /* allow notifications again */
+ g_object_thaw_notify (G_OBJECT (buffer));
+}
+
+
+
+void
+mousepad_view_convert_selection_case (MousepadView *view,
+ gint type)
+{
+ gchar *text;
+ gchar *converted;
+ GtkTextBuffer *buffer;
+ GtkTextIter start_iter, end_iter;
+ gint offset = -1;
+ GSList *li = NULL;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_VIEW (view));
+ mousepad_return_if_fail (mousepad_view_get_selection_length (view, NULL) > 0);
+
+ /* get the buffer */
+ buffer = mousepad_view_get_buffer (view);
+
+ /* begin a user action */
+ gtk_text_buffer_begin_user_action (buffer);
+
+ if (view->selection_marks == NULL)
+ {
+ /* get selection bounds */
+ gtk_text_buffer_get_selection_bounds (buffer, &start_iter, &end_iter);
+
+ /* get string offset */
+ offset = gtk_text_iter_get_offset (&start_iter);
+
+ /* enter the loop without hassle */
+ goto selection_enter;
+ }
+
+ /* replace all selected items */
+ for (li = view->selection_marks; li != NULL; li = li->next)
+ {
+ /* get iters from column selection */
+ gtk_text_buffer_get_iter_at_mark (buffer, &start_iter, li->data);
+ li = li->next;
+ gtk_text_buffer_get_iter_at_mark (buffer, &end_iter, li->data);
+
+ /* label for normal selections */
+ selection_enter:
+
+ /* get the selected string */
+ text = gtk_text_buffer_get_slice (buffer, &start_iter, &end_iter, FALSE);
+ if (G_LIKELY (text != NULL))
+ {
+ switch (type)
+ {
+ case LOWERCASE:
+ converted = g_utf8_strdown (text, -1);
+ break;
+
+ case UPPERCASE:
+ converted = g_utf8_strup (text, -1);
+ break;
+
+ case TITLECASE:
+ converted = mousepad_util_utf8_strcapital (text);
+ break;
+
+ case OPPOSITE_CASE:
+ converted = mousepad_util_utf8_stropposite (text);
+ break;
+
+ default:
+ mousepad_assert_not_reached ();
+ break;
+ }
+
+ /* only update the buffer if the string changed */
+ if (G_LIKELY (converted && strcmp (text, converted) != 0))
+ {
+ /* debug check */
+ mousepad_return_if_fail (g_utf8_validate (converted, -1, NULL));
+
+ /* delete old string */
+ gtk_text_buffer_delete (buffer, &start_iter, &end_iter);
+
+ /* insert string */
+ gtk_text_buffer_insert (buffer, &end_iter, converted, -1);
+ }
+
+ /* cleanup */
+ g_free (converted);
+ g_free (text);
+ }
+
+ /* leave for normal selections */
+ if (offset != -1)
+ break;
+ }
+
+ /* restore selection if needed */
+ if (offset != -1)
+ {
+ /* restore start iter */
+ gtk_text_buffer_get_iter_at_offset (buffer, &start_iter, offset);
+
+ /* select range */
+ gtk_text_buffer_select_range (buffer, &end_iter, &start_iter);
+ }
+ else
+ {
+ /* redraw column selection */
+ mousepad_view_selection_draw (view, FALSE);
+ }
+
+ /* end user action */
+ gtk_text_buffer_end_user_action (buffer);
+}
+
+
+
+void
+mousepad_view_convert_spaces_and_tabs (MousepadView *view,
+ gint type)
+{
+ GtkTextBuffer *buffer;
+ GtkTextMark *mark;
+ GtkTextIter start_iter, end_iter;
+ GtkTextIter iter;
+ gint tab_size;
+ gboolean in_range = FALSE;
+ gboolean no_forward;
+ gunichar c;
+ gint offset;
+ gint n_spaces = 0;
+ gint start_offset = -1;
+ gchar *string;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_VIEW (view));
+
+ /* get the buffer */
+ buffer = mousepad_view_get_buffer (view);
+
+ /* get the tab size */
+ tab_size = gtk_source_view_get_tab_width (GTK_SOURCE_VIEW (view));
+
+ /* get the start and end iter */
+ if (gtk_text_buffer_get_selection_bounds (buffer, &start_iter, &end_iter))
+ {
+ /* move to the start of the line when replacing spaces */
+ if (type == SPACES_TO_TABS && !gtk_text_iter_starts_line (&start_iter))
+ gtk_text_iter_set_line_offset (&start_iter, 0);
+
+ /* offset for restoring the selection afterwards */
+ start_offset = gtk_text_iter_get_offset (&start_iter);
+ }
+ else
+ {
+ /* get the document bounds */
+ gtk_text_buffer_get_bounds (buffer, &start_iter, &end_iter);
+ }
+
+ /* leave when the iters are equal (empty docs) */
+ if (gtk_text_iter_equal (&start_iter, &end_iter))
+ return;
+
+ /* begin a user action and free notifications */
+ g_object_freeze_notify (G_OBJECT (buffer));
+ gtk_text_buffer_begin_user_action (buffer);
+
+ /* create a mark to restore the end iter after modifieing the buffer */
+ mark = gtk_text_buffer_create_mark (buffer, NULL, &end_iter, FALSE);
+
+ /* walk the text between the iters */
+ for (;;)
+ {
+ /* get the character */
+ c = gtk_text_iter_get_char (&start_iter);
+
+ /* reset */
+ no_forward = FALSE;
+
+ if (type == SPACES_TO_TABS)
+ {
+ if (c == ' ' || in_range)
+ {
+ if (in_range == FALSE)
+ {
+ /* set the start iter */
+ iter = start_iter;
+
+ /* get the real offset of the start iter */
+ offset = mousepad_util_get_real_line_offset (&iter, tab_size);
+
+ /* the number of spaces to inline with the tabs */
+ n_spaces = tab_size - offset % tab_size;
+ }
+
+ /* check if we can already insert a tab */
+ if (n_spaces == 0)
+ {
+ /* delete the selected spaces */
+ gtk_text_buffer_delete (buffer, &iter, &start_iter);
+ gtk_text_buffer_insert (buffer, &start_iter, "\t", 1);
+
+ /* restore the end iter */
+ gtk_text_buffer_get_iter_at_mark (buffer, &end_iter, mark);
+
+ /* stop being inside a range */
+ in_range = FALSE;
+
+ /* no need to forward (iter moved after the insert */
+ no_forward = TRUE;
+ }
+ else
+ {
+ /* check whether we're still in a range */
+ in_range = (c == ' ');
+ }
+
+ /* decrease counter */
+ n_spaces--;
+ }
+
+ /* go to next line if we reached text */
+ if (!g_unichar_isspace (c))
+ {
+ /* continue to the next line if we hit non spaces */
+ gtk_text_iter_forward_line (&start_iter);
+
+ /* make sure there is no valid range anymore */
+ in_range = FALSE;
+
+ /* no need to forward */
+ no_forward = TRUE;
+ }
+ }
+ else if (type == TABS_TO_SPACES && c == '\t')
+ {
+ /* get the real offset of the iter */
+ offset = mousepad_util_get_real_line_offset (&start_iter, tab_size);
+
+ /* the number of spaces to inline with the tabs */
+ n_spaces = tab_size - offset % tab_size;
+
+ /* move one character forwards */
+ iter = start_iter;
+ gtk_text_iter_forward_char (&start_iter);
+
+ /* delete the tab */
+ gtk_text_buffer_delete (buffer, &iter, &start_iter);
+
+ /* create a string with the number of spaces */
+ string = g_strnfill (n_spaces, ' ');
+
+ /* insert the spaces */
+ gtk_text_buffer_insert (buffer, &start_iter, string, n_spaces);
+
+ /* cleanup */
+ g_free (string);
+
+ /* restore the end iter */
+ gtk_text_buffer_get_iter_at_mark (buffer, &end_iter, mark);
+
+ /* iter already moved by the insert */
+ no_forward = TRUE;
+ }
+
+ /* break when the iters are equal (or start is bigger) */
+ if (gtk_text_iter_compare (&start_iter, &end_iter) >= 0)
+ break;
+
+ /* forward the iter */
+ if (G_LIKELY (no_forward == FALSE))
+ gtk_text_iter_forward_char (&start_iter);
+ }
+
+ /* delete our mark */
+ gtk_text_buffer_delete_mark (buffer, mark);
+
+ /* restore the selection if needed */
+ if (start_offset > -1)
+ {
+ /* restore iter and select range */
+ gtk_text_buffer_get_iter_at_offset (buffer, &start_iter, start_offset);
+ gtk_text_buffer_select_range (buffer, &end_iter, &start_iter);
+ }
+
+ /* end the user action */
+ gtk_text_buffer_end_user_action (buffer);
+ g_object_thaw_notify (G_OBJECT (buffer));
+}
+
+
+
+void
+mousepad_view_strip_trailing_spaces (MousepadView *view)
+{
+ GtkTextBuffer *buffer;
+ GtkTextIter start_iter, end_iter, needle;
+ gint start, end, i;
+ gunichar c;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_VIEW (view));
+
+ /* get the buffer */
+ buffer = mousepad_view_get_buffer (view);
+
+ /* get range in line numbers */
+ if (gtk_text_buffer_get_selection_bounds (buffer, &start_iter, &end_iter))
+ {
+ start = gtk_text_iter_get_line (&start_iter);
+ end = gtk_text_iter_get_line (&end_iter) + 1;
+ }
+ else
+ {
+ start = 0;
+ end = gtk_text_buffer_get_line_count (buffer);
+ }
+
+ /* begin a user action and free notifications */
+ g_object_freeze_notify (G_OBJECT (buffer));
+ gtk_text_buffer_begin_user_action (buffer);
+
+ /* walk all the selected lines */
+ for (i = start; i < end; i++)
+ {
+ /* get the iter at the line */
+ gtk_text_buffer_get_iter_at_line (buffer, &end_iter, i);
+
+ /* continue if the line is empty */
+ if (gtk_text_iter_ends_line (&end_iter))
+ continue;
+
+ /* move the iter to the end of the line */
+ gtk_text_iter_forward_to_line_end (&end_iter);
+
+ /* initialize the iters */
+ needle = start_iter = end_iter;
+
+ /* walk backwards until we hit something else then a space or tab */
+ while (gtk_text_iter_backward_char (&needle))
+ {
+ /* get the character */
+ c = gtk_text_iter_get_char (&needle);
+
+ /* set the start iter of the spaces range or stop searching */
+ if (c == ' ' || c == '\t')
+ start_iter = needle;
+ else
+ break;
+ }
+
+ /* remove the spaces/tabs if the iters are not equal */
+ if (!gtk_text_iter_equal (&start_iter, &end_iter))
+ gtk_text_buffer_delete (buffer, &start_iter, &end_iter);
+ }
+
+ /* end the user action */
+ gtk_text_buffer_end_user_action (buffer);
+ g_object_thaw_notify (G_OBJECT (buffer));
+}
+
+
+
+void
+mousepad_view_move_selection (MousepadView *view,
+ gint type)
+{
+ GtkTextBuffer *buffer;
+ GtkTextIter start_iter, end_iter, iter;
+ GtkTextMark *mark;
+ gchar *text;
+ gboolean insert_eol;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_VIEW (view));
+
+ /* get the buffer */
+ buffer = mousepad_view_get_buffer (view);
+
+ /* begin a user action */
+ gtk_text_buffer_begin_user_action (buffer);
+
+ if (gtk_text_buffer_get_selection_bounds (buffer, &start_iter, &end_iter))
+ {
+ if (type == MOVE_LINE_UP)
+ {
+ /* useless action if we're already at the start of the buffer */
+ if (gtk_text_iter_get_line (&start_iter) == 0)
+ goto leave;
+
+ /* set insert point to end of selected line */
+ iter = end_iter;
+ if (!gtk_text_iter_ends_line (&iter))
+ gtk_text_iter_forward_to_line_end (&iter);
+
+ /* make start iter to previous line (can not fail) */
+ gtk_text_iter_backward_line (&start_iter);
+
+ /* move end iter to end of line */
+ end_iter = start_iter;
+ if (!gtk_text_iter_ends_line (&end_iter))
+ gtk_text_iter_forward_to_line_end (&end_iter);
+
+ /* move start iter one line back */
+ insert_eol = !gtk_text_iter_backward_char (&start_iter);
+ }
+ else /* MOVE_LINE_DOWN */
+ {
+ /* useless action if we're already at the end of the buffer */
+ if (gtk_text_iter_get_line (&end_iter) == gtk_text_buffer_get_line_count (buffer) - 1)
+ goto leave;
+
+ /* move insert iter to start of the line */
+ iter = start_iter;
+ if (!gtk_text_iter_starts_line (&iter))
+ gtk_text_iter_set_line_offset (&iter, 0);
+
+ /* move start iter to the start of the line below the selection */
+ start_iter = end_iter;
+ gtk_text_iter_forward_line (&start_iter);
+
+ /* set the end iter */
+ end_iter = start_iter;
+ insert_eol = !gtk_text_iter_forward_line (&end_iter);
+ }
+
+ /* create mark at insert point */
+ mark = gtk_text_buffer_create_mark (buffer, NULL, &iter, TRUE);
+
+ /* copy the line above the selection */
+ text = gtk_text_buffer_get_slice (buffer, &start_iter, &end_iter, FALSE);
+
+ /* delete the new line that we're going to insert later on */
+ if (insert_eol && type == MOVE_LINE_UP)
+ gtk_text_iter_forward_char (&end_iter);
+ else if (insert_eol && type == MOVE_LINE_DOWN)
+ gtk_text_iter_backward_char (&start_iter);
+
+ /* delete */
+ gtk_text_buffer_delete (buffer, &start_iter, &end_iter);
+
+ /* restore mark */
+ gtk_text_buffer_get_iter_at_mark (buffer, &iter, mark);
+
+ /* insert new line if needed */
+ if (insert_eol && type == MOVE_LINE_UP)
+ gtk_text_buffer_insert (buffer, &iter, "\n", 1);
+
+ /* insert text */
+ gtk_text_buffer_insert (buffer, &iter, text, -1);
+
+ /* insert new line if needed */
+ if (insert_eol && type == MOVE_LINE_DOWN)
+ gtk_text_buffer_insert (buffer, &iter, "\n", 1);
+
+ /* cleanup */
+ g_free (text);
+
+ /* sometimes we need to restore the selection (left gravity of the selection marks) */
+ if (type == MOVE_LINE_UP)
+ {
+ /* get selection bounds */
+ gtk_text_buffer_get_selection_bounds (buffer, &start_iter, &end_iter);
+
+ /* check if we need to restore the selected range */
+ if (gtk_text_iter_equal (&iter, &end_iter))
+ {
+ /* restore end iter from mark and select the range again */
+ gtk_text_buffer_get_iter_at_mark (buffer, &end_iter, mark);
+ gtk_text_buffer_select_range (buffer, &start_iter, &end_iter);
+ }
+ }
+
+ /* delete mark */
+ gtk_text_buffer_delete_mark (buffer, mark);
+ }
+
+ /* labeltje */
+ leave:
+
+ /* end the action */
+ gtk_text_buffer_end_user_action (buffer);
+
+ /* show */
+ mousepad_view_scroll_to_cursor (view);
+}
+
+
+
+void
+mousepad_view_duplicate (MousepadView *view)
+{
+ GtkTextBuffer *buffer;
+ GtkTextIter start_iter, end_iter;
+ gboolean has_selection;
+ gboolean insert_eol = FALSE;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_VIEW (view));
+
+ /* get the buffer */
+ buffer = mousepad_view_get_buffer (view);
+
+ /* begin a user action */
+ gtk_text_buffer_begin_user_action (buffer);
+
+ /* get iters */
+ has_selection = gtk_text_buffer_get_selection_bounds (buffer, &start_iter, &end_iter);
+
+ /* select entire line */
+ if (has_selection == FALSE)
+ {
+ /* set to the start of the line */
+ if (!gtk_text_iter_starts_line (&start_iter))
+ gtk_text_iter_set_line_offset (&start_iter, 0);
+
+ /* move the other iter to the start of the next line */
+ insert_eol = !gtk_text_iter_forward_line (&end_iter);
+ }
+
+ /* insert a dupplicate of the text before the iter */
+ gtk_text_buffer_insert_range (buffer, &start_iter, &start_iter, &end_iter);
+
+ /* insert a new line if needed */
+ if (insert_eol)
+ gtk_text_buffer_insert (buffer, &start_iter, "\n", 1);
+
+ /* end the action */
+ gtk_text_buffer_end_user_action (buffer);
+}
+
+
+
+void
+mousepad_view_indent (MousepadView *view,
+ gint type)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_VIEW (view));
+
+ /* run a forced indent of the line(s) */
+ mousepad_view_indent_selection (view, type == INCREASE_INDENT, TRUE);
+}
+
+
+
+void
+mousepad_view_set_line_numbers (MousepadView *view,
+ gboolean line_numbers)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_VIEW (view));
+
+ gtk_source_view_set_show_line_numbers (GTK_SOURCE_VIEW (view), line_numbers);
+}
+
+
+
+void
+mousepad_view_set_auto_indent (MousepadView *view,
+ gboolean auto_indent)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_VIEW (view));
+
+ gtk_source_view_set_auto_indent (GTK_SOURCE_VIEW (view), auto_indent);
+}
+
+
+
+void
+mousepad_view_set_tab_size (MousepadView *view,
+ gint tab_size)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_VIEW (view));
+ mousepad_return_if_fail (GTK_IS_TEXT_VIEW (view));
+
+ gtk_source_view_set_tab_width (GTK_SOURCE_VIEW (view), tab_size);
+}
+
+
+
+void
+mousepad_view_set_insert_spaces (MousepadView *view,
+ gboolean insert_spaces)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_VIEW (view));
+
+ gtk_source_view_set_insert_spaces_instead_of_tabs (GTK_SOURCE_VIEW (view), insert_spaces);
+}
+
+
+
+gboolean
+mousepad_view_get_selection_length (MousepadView *view,
+ gboolean *is_column_selection)
+{
+ GtkTextBuffer *buffer;
+ GtkTextIter sel_start, sel_end;
+ gint sel_length = 0;
+ gboolean column_selection;
+
+ mousepad_return_val_if_fail (MOUSEPAD_IS_VIEW (view), FALSE);
+
+ /* get the text buffer */
+ buffer = mousepad_view_get_buffer (view);
+
+ /* whether this is a column selection */
+ column_selection = view->selection_marks != NULL || view->selection_start_x != -1;
+
+ /* we have a vertical selection */
+ if (column_selection)
+ {
+ /* get the selection count from the selection draw function */
+ sel_length = view->selection_length;
+ }
+ else if (gtk_text_buffer_get_selection_bounds (buffer, &sel_start, &sel_end))
+ {
+ /* calculate the selection length from the iter offset (absolute) */
+ sel_length = ABS (gtk_text_iter_get_offset (&sel_end) - gtk_text_iter_get_offset (&sel_start));
+ }
+
+ /* whether this is a column selection */
+ if (is_column_selection)
+ *is_column_selection = column_selection;
+
+ /* return length */
+ return sel_length;
+}
+
+
+
+gboolean
+mousepad_view_get_line_numbers (MousepadView *view)
+{
+ mousepad_return_val_if_fail (MOUSEPAD_IS_VIEW (view), FALSE);
+
+ return gtk_source_view_get_show_line_numbers (GTK_SOURCE_VIEW (view));
+}
+
+
+
+gboolean
+mousepad_view_get_auto_indent (MousepadView *view)
+{
+ mousepad_return_val_if_fail (MOUSEPAD_IS_VIEW (view), FALSE);
+
+ return gtk_source_view_get_auto_indent (GTK_SOURCE_VIEW (view));
+}
+
+
+
+gint
+mousepad_view_get_tab_size (MousepadView *view)
+{
+ mousepad_return_val_if_fail (MOUSEPAD_IS_VIEW (view), -1);
+
+ return gtk_source_view_get_tab_width (GTK_SOURCE_VIEW (view));
+}
+
+
+
+gboolean
+mousepad_view_get_insert_spaces (MousepadView *view)
+{
+ mousepad_return_val_if_fail (MOUSEPAD_IS_VIEW (view), FALSE);
+
+ return gtk_source_view_get_insert_spaces_instead_of_tabs (GTK_SOURCE_VIEW (view));
+}
diff --git a/mousepad/mousepad-view.h b/mousepad/mousepad-view.h
new file mode 100644
index 0000000..109f7f0
--- /dev/null
+++ b/mousepad/mousepad-view.h
@@ -0,0 +1,121 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __MOUSEPAD_VIEW_H__
+#define __MOUSEPAD_VIEW_H__
+
+G_BEGIN_DECLS
+
+#define MOUSEPAD_TYPE_VIEW (mousepad_view_get_type ())
+#define MOUSEPAD_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MOUSEPAD_TYPE_VIEW, MousepadView))
+#define MOUSEPAD_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MOUSEPAD_TYPE_VIEW, MousepadViewClass))
+#define MOUSEPAD_IS_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MOUSEPAD_TYPE_VIEW))
+#define MOUSEPAD_IS_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MOUSEPADL_TYPE_VIEW))
+#define MOUSEPAD_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MOUSEPAD_TYPE_VIEW, MousepadViewClass))
+
+typedef struct _MousepadViewClass MousepadViewClass;
+typedef struct _MousepadView MousepadView;
+typedef enum _MousepadViewFlags MousepadViewFlags;
+
+enum
+{
+ LOWERCASE,
+ UPPERCASE,
+ TITLECASE,
+ OPPOSITE_CASE
+};
+
+enum
+{
+ SPACES_TO_TABS,
+ TABS_TO_SPACES
+};
+
+enum
+{
+ MOVE_LINE_UP,
+ MOVE_LINE_DOWN
+};
+
+enum
+{
+ INCREASE_INDENT,
+ DECREASE_INDENT
+};
+
+GType mousepad_view_get_type (void) G_GNUC_CONST;
+
+void mousepad_view_scroll_to_cursor (MousepadView *view);
+
+void mousepad_view_transpose (MousepadView *view);
+
+void mousepad_view_clipboard_cut (MousepadView *view);
+
+void mousepad_view_clipboard_copy (MousepadView *view);
+
+void mousepad_view_clipboard_paste (MousepadView *view,
+ const gchar *string,
+ gboolean paste_as_column);
+
+void mousepad_view_delete_selection (MousepadView *view);
+
+void mousepad_view_select_all (MousepadView *view);
+
+void mousepad_view_change_selection (MousepadView *view);
+
+void mousepad_view_convert_selection_case (MousepadView *view,
+ gint type);
+
+void mousepad_view_convert_spaces_and_tabs (MousepadView *view,
+ gint type);
+
+void mousepad_view_strip_trailing_spaces (MousepadView *view);
+
+void mousepad_view_move_selection (MousepadView *view,
+ gint type);
+
+void mousepad_view_duplicate (MousepadView *view);
+
+void mousepad_view_indent (MousepadView *view,
+ gint type);
+
+void mousepad_view_set_line_numbers (MousepadView *view,
+ gboolean line_numbers);
+
+void mousepad_view_set_auto_indent (MousepadView *view,
+ gboolean auto_indent);
+
+void mousepad_view_set_tab_size (MousepadView *view,
+ gint tab_size);
+
+void mousepad_view_set_insert_spaces (MousepadView *view,
+ gboolean insert_spaces);
+
+gint mousepad_view_get_selection_length (MousepadView *view,
+ gboolean *is_column_selection);
+
+gboolean mousepad_view_get_line_numbers (MousepadView *view);
+
+gboolean mousepad_view_get_auto_indent (MousepadView *view);
+
+gint mousepad_view_get_tab_size (MousepadView *view);
+
+gboolean mousepad_view_get_insert_spaces (MousepadView *view);
+
+
+G_END_DECLS
+
+#endif /* !__MOUSEPAD_VIEW_H__ */
diff --git a/mousepad/mousepad-window-ui.xml b/mousepad/mousepad-window-ui.xml
new file mode 100644
index 0000000..f960bc6
--- /dev/null
+++ b/mousepad/mousepad-window-ui.xml
@@ -0,0 +1,159 @@
+<ui>
+ <!--
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ Place, Suite 330, Boston, MA 02111-1307 USA
+ -->
+
+ <menubar name="main-menu">
+ <menu action="file-menu">
+ <menuitem action="new" />
+ <menuitem action="new-window" />
+ <menuitem action="template-menu" />
+ <separator />
+ <menuitem action="open" />
+ <menu action="recent-menu">
+ <menuitem action="no-recent-items" />
+ <placeholder name="placeholder-recent-items" />
+ <separator />
+ <menuitem action="clear-recent" />
+ </menu>
+ <separator />
+ <menuitem action="save" />
+ <menuitem action="save-as" />
+ <menuitem action="save-all" />
+ <menuitem action="revert" />
+ <separator />
+ <menuitem action="print" />
+ <separator />
+ <menuitem action="detach" />
+ <separator />
+ <menuitem action="close" />
+ <menuitem action="close-window" />
+ </menu>
+
+ <menu action="edit-menu">
+ <menuitem action="undo" />
+ <menuitem action="redo" />
+ <separator />
+ <menuitem action="cut" />
+ <menuitem action="copy" />
+ <menuitem action="paste" />
+ <menu action="paste-menu">
+ <menuitem action="paste-history" />
+ <menuitem action="paste-column" />
+ </menu>
+ <menuitem action="delete" />
+ <separator />
+ <menuitem action="select-all" />
+ <menuitem action="change-selection" />
+ <separator />
+ <menuitem action="find" />
+ <menuitem action="find-next" />
+ <menuitem action="find-previous" />
+ <menuitem action="replace" />
+ </menu>
+
+ <menu action="view-menu">
+ <menuitem action="font" />
+ <separator />
+ <menu action="color-scheme-menu">
+ <placeholder name="placeholder-color-scheme-items" />
+ </menu>
+ <menuitem action="line-numbers" />
+ <separator />
+ <menuitem action="statusbar" />
+ </menu>
+
+ <menu action="text-menu">
+ <menu action="convert-menu">
+ <menuitem action="lowercase" />
+ <menuitem action="uppercase" />
+ <menuitem action="titlecase" />
+ <menuitem action="opposite-case" />
+ <separator />
+ <menuitem action="tabs-to-spaces" />
+ <menuitem action="spaces-to-tabs" />
+ <separator />
+ <menuitem action="strip-trailing" />
+ <separator />
+ <menuitem action="transpose" />
+ </menu>
+ <menu action="move-menu">
+ <menuitem action="line-up" />
+ <menuitem action="line-down" />
+ </menu>
+ <separator />
+ <menuitem action="duplicate" />
+ <separator />
+ <menuitem action="increase-indent" />
+ <menuitem action="decrease-indent" />
+ </menu>
+
+ <menu action="document-menu">
+ <menuitem action="auto-indent" />
+ <menu action="eol-menu">
+ <menuitem action="unix" />
+ <menuitem action="mac" />
+ <menuitem action="dos" />
+ </menu>
+ <menu action="tab-size-menu">
+ <placeholder name="placeholder-tab-items" />
+ <separator />
+ <menuitem action="insert-spaces" />
+ </menu>
+ <menuitem action="word-wrap" />
+ <separator />
+ <menuitem action="write-bom" />
+ <separator />
+ <menu action="language-menu">
+ <placeholder name="placeholder-language-section-items" />
+ </menu>
+ </menu>
+
+ <menu action="navigation-menu">
+ <menuitem action="back" />
+ <menuitem action="forward" />
+ <separator />
+ <placeholder name="placeholder-file-items" />
+ <separator />
+ <menuitem action="go-to" />
+ </menu>
+
+ <menu action="help-menu">
+ <menuitem action="contents" />
+ <menuitem action="about" />
+ </menu>
+ </menubar>
+
+ <popup action="tab-menu">
+ <menuitem action="save" />
+ <menuitem action="save-as" />
+ <separator />
+ <menuitem action="revert" />
+ <separator />
+ <menuitem action="detach" />
+ <menuitem action="close" />
+ </popup>
+
+ <popup action="textview-menu">
+ <menuitem action="undo" />
+ <menuitem action="redo" />
+ <separator />
+ <menuitem action="cut" />
+ <menuitem action="copy" />
+ <menuitem action="paste" />
+ <separator />
+ <menuitem action="select-all" />
+ </popup>
+</ui>
diff --git a/mousepad/mousepad-window.c b/mousepad/mousepad-window.c
new file mode 100644
index 0000000..3067788
--- /dev/null
+++ b/mousepad/mousepad-window.c
@@ -0,0 +1,5102 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include <glib/gstdio.h>
+
+#include <mousepad/mousepad-private.h>
+#include <mousepad/mousepad-application.h>
+#include <mousepad/mousepad-marshal.h>
+#include <mousepad/mousepad-document.h>
+#include <mousepad/mousepad-dialogs.h>
+#include <mousepad/mousepad-preferences.h>
+#include <mousepad/mousepad-replace-dialog.h>
+#include <mousepad/mousepad-encoding-dialog.h>
+#include <mousepad/mousepad-search-bar.h>
+#include <mousepad/mousepad-statusbar.h>
+#include <mousepad/mousepad-print.h>
+#include <mousepad/mousepad-window.h>
+#include <mousepad/mousepad-window-ui.h>
+
+
+
+#define PADDING (2)
+#define PASTE_HISTORY_MENU_LENGTH (30)
+
+#if GTK_CHECK_VERSION (2,12,0)
+static gconstpointer NOTEBOOK_GROUP = "Mousepad";
+#endif
+
+
+
+enum
+{
+ NEW_WINDOW,
+ NEW_WINDOW_WITH_DOCUMENT,
+ LAST_SIGNAL
+};
+
+
+
+static void mousepad_window_dispose (GObject *object);
+static void mousepad_window_finalize (GObject *object);
+static gboolean mousepad_window_configure_event (GtkWidget *widget,
+ GdkEventConfigure *event);
+
+/* statusbar tooltips */
+static void mousepad_window_connect_proxy (GtkUIManager *manager,
+ GtkAction *action,
+ GtkWidget *proxy,
+ MousepadWindow *window);
+static void mousepad_window_disconnect_proxy (GtkUIManager *manager,
+ GtkAction *action,
+ GtkWidget *proxy,
+ MousepadWindow *window);
+static void mousepad_window_menu_item_selected (GtkWidget *menu_item,
+ MousepadWindow *window);
+static void mousepad_window_menu_item_deselected (GtkWidget *menu_item,
+ MousepadWindow *window);
+
+/* save windows geometry */
+static gboolean mousepad_window_save_geometry_timer (gpointer user_data);
+static void mousepad_window_save_geometry_timer_destroy (gpointer user_data);
+
+/* window functions */
+static gboolean mousepad_window_open_file (MousepadWindow *window,
+ const gchar *filename,
+ MousepadEncoding encoding);
+static gboolean mousepad_window_close_document (MousepadWindow *window,
+ MousepadDocument *document);
+static void mousepad_window_set_title (MousepadWindow *window);
+static void mousepad_window_populate_statusbar_popup (MousepadWindow *window,
+ GtkMenu *menu,
+ MousepadStatusbar *statusbar);
+static void mousepad_window_statusbar_filetype_toggled (GtkCheckMenuItem *item,
+ MousepadWindow *window);
+
+/* notebook signals */
+static void mousepad_window_notebook_switch_page (GtkNotebook *notebook,
+ GtkNotebookPage *page,
+ guint page_num,
+ MousepadWindow *window);
+static void mousepad_window_notebook_reordered (GtkNotebook *notebook,
+ GtkWidget *page,
+ guint page_num,
+ MousepadWindow *window);
+static void mousepad_window_notebook_added (GtkNotebook *notebook,
+ GtkWidget *page,
+ guint page_num,
+ MousepadWindow *window);
+static void mousepad_window_notebook_removed (GtkNotebook *notebook,
+ GtkWidget *page,
+ guint page_num,
+ MousepadWindow *window);
+static void mousepad_window_notebook_menu_position (GtkMenu *menu,
+ gint *x,
+ gint *y,
+ gboolean *push_in,
+ gpointer user_data);
+static gboolean mousepad_window_notebook_button_release_event (GtkNotebook *notebook,
+ GdkEventButton *event,
+ MousepadWindow *window);
+static gboolean mousepad_window_notebook_button_press_event (GtkNotebook *notebook,
+ GdkEventButton *event,
+ MousepadWindow *window);
+#if GTK_CHECK_VERSION (2,12,0)
+static GtkNotebook *mousepad_window_notebook_create_window (GtkNotebook *notebook,
+ GtkWidget *page,
+ gint x,
+ gint y,
+ MousepadWindow *window);
+#endif
+
+/* document signals */
+static void mousepad_window_modified_changed (MousepadWindow *window);
+static void mousepad_window_cursor_changed (MousepadDocument *document,
+ gint line,
+ gint column,
+ gint selection,
+ MousepadWindow *window);
+static void mousepad_window_selection_changed (MousepadDocument *document,
+ gint selection,
+ MousepadWindow *window);
+static void mousepad_window_overwrite_changed (MousepadDocument *document,
+ gboolean overwrite,
+ MousepadWindow *window);
+static void mousepad_window_language_changed (MousepadDocument *document,
+ GtkSourceLanguage *language,
+ MousepadWindow *window);
+static void mousepad_window_can_undo (MousepadWindow *window,
+ GParamSpec *unused,
+ GObject *buffer);
+static void mousepad_window_can_redo (MousepadWindow *window,
+ GParamSpec *unused,
+ GObject *buffer);
+
+/* menu functions */
+static void mousepad_window_menu_templates_fill (MousepadWindow *window,
+ GtkWidget *menu,
+ const gchar *path);
+static void mousepad_window_menu_templates (GtkWidget *item,
+ MousepadWindow *window);
+static void mousepad_window_menu_tab_sizes (MousepadWindow *window);
+static void mousepad_window_menu_tab_sizes_update (MousepadWindow *window);
+static void mousepad_window_menu_textview_deactivate (GtkWidget *menu,
+ GtkTextView *textview);
+static void mousepad_window_menu_textview_popup (GtkTextView *textview,
+ GtkMenu *old_menu,
+ MousepadWindow *window);
+static void mousepad_window_update_actions (MousepadWindow *window);
+static gboolean mousepad_window_update_gomenu_idle (gpointer user_data);
+static void mousepad_window_update_gomenu_idle_destroy (gpointer user_data);
+static void mousepad_window_update_gomenu (MousepadWindow *window);
+static void mousepad_window_menu_color_schemes (MousepadWindow *window);
+static void mousepad_window_menu_languages (MousepadWindow *window);
+
+/* recent functions */
+static void mousepad_window_recent_add (MousepadWindow *window,
+ MousepadFile *file);
+static gint mousepad_window_recent_sort (GtkRecentInfo *a,
+ GtkRecentInfo *b);
+static void mousepad_window_recent_manager_init (MousepadWindow *window);
+static gboolean mousepad_window_recent_menu_idle (gpointer user_data);
+static void mousepad_window_recent_menu_idle_destroy (gpointer user_data);
+static void mousepad_window_recent_menu (MousepadWindow *window);
+static const gchar *mousepad_window_recent_get_charset (GtkRecentInfo *info);
+static void mousepad_window_recent_clear (MousepadWindow *window);
+
+/* dnd */
+static void mousepad_window_drag_data_received (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ GtkSelectionData *selection_data,
+ guint info,
+ guint drag_time,
+ MousepadWindow *window);
+
+/* search bar */
+static void mousepad_window_hide_search_bar (MousepadWindow *window);
+
+/* history clipboard functions */
+static void mousepad_window_paste_history_add (MousepadWindow *window);
+static void mousepad_window_paste_history_menu_position (GtkMenu *menu,
+ gint *x,
+ gint *y,
+ gboolean *push_in,
+ gpointer user_data);
+static void mousepad_window_paste_history_activate (GtkMenuItem *item,
+ MousepadWindow *window);
+static GtkWidget *mousepad_window_paste_history_menu_item (const gchar *text,
+ const gchar *mnemonic);
+static GtkWidget *mousepad_window_paste_history_menu (MousepadWindow *window);
+
+/* miscellaneous actions */
+static void mousepad_window_button_close_tab (MousepadDocument *document,
+ MousepadWindow *window);
+static gboolean mousepad_window_delete_event (MousepadWindow *window,
+ GdkEvent *event);
+
+/* actions */
+static void mousepad_window_action_new (GtkAction *action,
+ MousepadWindow *window);
+static void mousepad_window_action_new_window (GtkAction *action,
+ MousepadWindow *window);
+static void mousepad_window_action_new_from_template (GtkMenuItem *item,
+ MousepadWindow *window);
+static void mousepad_window_action_open (GtkAction *action,
+ MousepadWindow *window);
+static void mousepad_window_action_open_recent (GtkAction *action,
+ MousepadWindow *window);
+static void mousepad_window_action_clear_recent (GtkAction *action,
+ MousepadWindow *window);
+static gboolean mousepad_window_action_save (GtkAction *action,
+ MousepadWindow *window);
+static gboolean mousepad_window_action_save_as (GtkAction *action,
+ MousepadWindow *window);
+static void mousepad_window_action_save_all (GtkAction *action,
+ MousepadWindow *window);
+static void mousepad_window_action_revert (GtkAction *action,
+ MousepadWindow *window);
+static void mousepad_window_action_print (GtkAction *action,
+ MousepadWindow *window);
+static void mousepad_window_action_detach (GtkAction *action,
+ MousepadWindow *window);
+static void mousepad_window_action_close (GtkAction *action,
+ MousepadWindow *window);
+static void mousepad_window_action_close_window (GtkAction *action,
+ MousepadWindow *window);
+static void mousepad_window_action_undo (GtkAction *action,
+ MousepadWindow *window);
+static void mousepad_window_action_redo (GtkAction *action,
+ MousepadWindow *window);
+static void mousepad_window_action_cut (GtkAction *action,
+ MousepadWindow *window);
+static void mousepad_window_action_copy (GtkAction *action,
+ MousepadWindow *window);
+static void mousepad_window_action_paste (GtkAction *action,
+ MousepadWindow *window);
+static void mousepad_window_action_paste_history (GtkAction *action,
+ MousepadWindow *window);
+static void mousepad_window_action_paste_column (GtkAction *action,
+ MousepadWindow *window);
+static void mousepad_window_action_delete (GtkAction *action,
+ MousepadWindow *window);
+static void mousepad_window_action_select_all (GtkAction *action,
+ MousepadWindow *window);
+static void mousepad_window_action_change_selection (GtkAction *action,
+ MousepadWindow *window);
+static void mousepad_window_action_find (GtkAction *action,
+ MousepadWindow *window);
+static void mousepad_window_action_find_next (GtkAction *action,
+ MousepadWindow *window);
+static void mousepad_window_action_find_previous (GtkAction *action,
+ MousepadWindow *window);
+static void mousepad_window_action_replace_destroy (MousepadWindow *window);
+static void mousepad_window_action_replace (GtkAction *action,
+ MousepadWindow *window);
+static void mousepad_window_action_select_font (GtkAction *action,
+ MousepadWindow *window);
+static void mousepad_window_action_color_scheme (GtkToggleAction *action,
+ MousepadWindow *window);
+static void mousepad_window_action_line_numbers (GtkToggleAction *action,
+ MousepadWindow *window);
+static void mousepad_window_action_statusbar_overwrite (MousepadWindow *window,
+ gboolean overwrite);
+static void mousepad_window_action_statusbar (GtkToggleAction *action,
+ MousepadWindow *window);
+static void mousepad_window_action_lowercase (GtkAction *action,
+ MousepadWindow *window);
+static void mousepad_window_action_uppercase (GtkAction *action,
+ MousepadWindow *window);
+static void mousepad_window_action_titlecase (GtkAction *action,
+ MousepadWindow *window);
+static void mousepad_window_action_opposite_case (GtkAction *action,
+ MousepadWindow *window);
+static void mousepad_window_action_tabs_to_spaces (GtkAction *action,
+ MousepadWindow *window);
+static void mousepad_window_action_spaces_to_tabs (GtkAction *action,
+ MousepadWindow *window);
+static void mousepad_window_action_strip_trailing_spaces (GtkAction *action,
+ MousepadWindow *window);
+static void mousepad_window_action_transpose (GtkAction *action,
+ MousepadWindow *window);
+static void mousepad_window_action_move_line_up (GtkAction *action,
+ MousepadWindow *window);
+static void mousepad_window_action_move_line_down (GtkAction *action,
+ MousepadWindow *window);
+static void mousepad_window_action_duplicate (GtkAction *action,
+ MousepadWindow *window);
+static void mousepad_window_action_increase_indent (GtkAction *action,
+ MousepadWindow *window);
+static void mousepad_window_action_decrease_indent (GtkAction *action,
+ MousepadWindow *window);
+static void mousepad_window_action_auto_indent (GtkToggleAction *action,
+ MousepadWindow *window);
+static void mousepad_window_action_line_ending (GtkRadioAction *action,
+ GtkRadioAction *current,
+ MousepadWindow *window);
+static void mousepad_window_action_tab_size (GtkToggleAction *action,
+ MousepadWindow *window);
+static void mousepad_window_action_word_wrap (GtkToggleAction *action,
+ MousepadWindow *window);
+static void mousepad_window_action_write_bom (GtkToggleAction *action,
+ MousepadWindow *window);
+static void mousepad_window_action_language (GtkToggleAction *action,
+ MousepadWindow *window);
+static void mousepad_window_action_insert_spaces (GtkToggleAction *action,
+ MousepadWindow *window);
+static void mousepad_window_action_prev_tab (GtkAction *action,
+ MousepadWindow *window);
+static void mousepad_window_action_next_tab (GtkAction *action,
+ MousepadWindow *window);
+static void mousepad_window_action_go_to_tab (GtkRadioAction *action,
+ GtkNotebook *notebook);
+static void mousepad_window_action_go_to_position (GtkAction *action,
+ MousepadWindow *window);
+static void mousepad_window_action_contents (GtkAction *action,
+ MousepadWindow *window);
+static void mousepad_window_action_about (GtkAction *action,
+ MousepadWindow *window);
+
+
+
+struct _MousepadWindowClass
+{
+ GtkWindowClass __parent__;
+};
+
+struct _MousepadWindow
+{
+ GtkWindow __parent__;
+
+ /* mousepad preferences */
+ MousepadPreferences *preferences;
+
+ /* the current active document */
+ MousepadDocument *active;
+
+ /* closures for the menu callbacks */
+ GClosure *menu_item_selected_closure;
+ GClosure *menu_item_deselected_closure;
+
+ /* action group */
+ GtkActionGroup *action_group;
+
+ /* recent manager */
+ GtkRecentManager *recent_manager;
+
+ /* UI manager */
+ GtkUIManager *ui_manager;
+ guint gomenu_merge_id;
+ guint recent_merge_id;
+
+ /* main window widgets */
+ GtkWidget *box;
+ GtkWidget *notebook;
+ GtkWidget *search_bar;
+ GtkWidget *statusbar;
+ GtkWidget *replace_dialog;
+
+ /* support to remember window geometry */
+ guint save_geometry_timer_id;
+
+ /* idle update functions for the recent and go menu */
+ guint update_recent_menu_id;
+ guint update_go_menu_id;
+};
+
+
+
+static const GtkActionEntry action_entries[] =
+{
+ { "file-menu", NULL, N_("_File"), NULL, NULL, NULL, },
+ { "new", GTK_STOCK_NEW, N_("_New"), "<control>N", N_("Create a new document"), G_CALLBACK (mousepad_window_action_new), },
+ { "new-window", NULL, N_("New _Window"), "<shift><control>N", N_("Create a new document in a new window"), G_CALLBACK (mousepad_window_action_new_window), },
+ { "template-menu", NULL, N_("New From Te_mplate"), NULL, NULL, NULL, },
+ { "open", GTK_STOCK_OPEN, N_("_Open..."), NULL, N_("Open a file"), G_CALLBACK (mousepad_window_action_open), },
+ { "recent-menu", NULL, N_("Op_en Recent"), NULL, NULL, NULL, },
+ { "no-recent-items", NULL, N_("No items found"), NULL, NULL, NULL, },
+ { "clear-recent", GTK_STOCK_CLEAR, N_("Clear _History"), NULL, N_("Clear the recently used files history"), G_CALLBACK (mousepad_window_action_clear_recent), },
+ { "save", GTK_STOCK_SAVE, NULL, "<control>S", N_("Save the current document"), G_CALLBACK (mousepad_window_action_save), },
+ { "save-as", GTK_STOCK_SAVE_AS, N_("Save _As..."), "<shift><control>S", N_("Save current document as another file"), G_CALLBACK (mousepad_window_action_save_as), },
+ { "save-all", NULL, N_("Save A_ll"), NULL, N_("Save all document in this window"), G_CALLBACK (mousepad_window_action_save_all), },
+ { "revert", GTK_STOCK_REVERT_TO_SAVED, N_("Re_vert"), NULL, N_("Revert to the saved version of the file"), G_CALLBACK (mousepad_window_action_revert), },
+ { "print", GTK_STOCK_PRINT, N_("_Print..."), "<control>P", N_("Print the current document"), G_CALLBACK (mousepad_window_action_print), },
+ { "detach", NULL, N_("_Detach Tab"), "<control>D", N_("Move the current document to a new window"), G_CALLBACK (mousepad_window_action_detach), },
+ { "close", GTK_STOCK_CLOSE, N_("Close _Tab"), "<control>W", N_("Close the current document"), G_CALLBACK (mousepad_window_action_close), },
+ { "close-window", GTK_STOCK_QUIT, N_("_Close Window"), "<control>Q", N_("Close this window"), G_CALLBACK (mousepad_window_action_close_window), },
+
+ { "edit-menu", NULL, N_("_Edit"), NULL, NULL, NULL, },
+ { "undo", GTK_STOCK_UNDO, NULL, "<control>Z", N_("Undo the last action"), G_CALLBACK (mousepad_window_action_undo), },
+ { "redo", GTK_STOCK_REDO, NULL, "<control>Y", N_("Redo the last undone action"), G_CALLBACK (mousepad_window_action_redo), },
+ { "cut", GTK_STOCK_CUT, NULL, NULL, N_("Cut the selection"), G_CALLBACK (mousepad_window_action_cut), },
+ { "copy", GTK_STOCK_COPY, NULL, NULL, N_("Copy the selection"), G_CALLBACK (mousepad_window_action_copy), },
+ { "paste", GTK_STOCK_PASTE, NULL, NULL, N_("Paste the clipboard"), G_CALLBACK (mousepad_window_action_paste), },
+ { "paste-menu", NULL, N_("Paste _Special"), NULL, NULL, NULL, },
+ { "paste-history", NULL, N_("Paste from _History"), NULL, N_("Paste from the clipboard history"), G_CALLBACK (mousepad_window_action_paste_history), },
+ { "paste-column", NULL, N_("Paste as _Column"), NULL, N_("Paste the clipboard text into a column"), G_CALLBACK (mousepad_window_action_paste_column), },
+ { "delete", GTK_STOCK_DELETE, NULL, NULL, N_("Delete the current selection"), G_CALLBACK (mousepad_window_action_delete), },
+ { "select-all", GTK_STOCK_SELECT_ALL, NULL, NULL, N_("Select the text in the entire document"), G_CALLBACK (mousepad_window_action_select_all), },
+ { "change-selection", NULL, N_("Change the selection"), NULL, N_("Change a normal selection into a column selection and vice versa"), G_CALLBACK (mousepad_window_action_change_selection), },
+ { "find", GTK_STOCK_FIND, NULL, NULL, N_("Search for text"), G_CALLBACK (mousepad_window_action_find), },
+ { "find-next", NULL, N_("Find _Next"), "F3", N_("Search forwards for the same text"), G_CALLBACK (mousepad_window_action_find_next), },
+ { "find-previous", NULL, N_("Find _Previous"), "<shift>F3", N_("Search backwards for the same text"), G_CALLBACK (mousepad_window_action_find_previous), },
+ { "replace", GTK_STOCK_FIND_AND_REPLACE, N_("Find and Rep_lace..."), NULL, N_("Search for and replace text"), G_CALLBACK (mousepad_window_action_replace), },
+
+ { "view-menu", NULL, N_("_View"), NULL, NULL, NULL, },
+ { "font", GTK_STOCK_SELECT_FONT, N_("Select F_ont..."), NULL, N_("Change the editor font"), G_CALLBACK (mousepad_window_action_select_font), },
+ { "color-scheme-menu", NULL, N_("_Color Scheme"), NULL, NULL, NULL, },
+
+ { "text-menu", NULL, N_("_Text"), NULL, NULL, NULL, },
+ { "convert-menu", NULL, N_("_Convert"), NULL, NULL, NULL, },
+ { "uppercase", NULL, N_("to _Uppercase"), NULL, N_("Change the case of the selection to uppercase"), G_CALLBACK (mousepad_window_action_uppercase), },
+ { "lowercase", NULL, N_("to _Lowercase"), NULL, N_("Change the case of the selection to lowercase"), G_CALLBACK (mousepad_window_action_lowercase), },
+ { "titlecase", NULL, N_("to _Title Case"), NULL, N_("Change the case of the selection to title case"), G_CALLBACK (mousepad_window_action_titlecase), },
+ { "opposite-case", NULL, N_("to _Opposite Case"), NULL, N_("Change the case of the selection opposite case"), G_CALLBACK (mousepad_window_action_opposite_case), },
+ { "tabs-to-spaces", NULL, N_("_Tabs to Spaces"), NULL, N_("Convert all tabs to spaces in the selection or document"), G_CALLBACK (mousepad_window_action_tabs_to_spaces), },
+ { "spaces-to-tabs", NULL, N_("_Spaces to Tabs"), NULL, N_("Convert all the leading spaces to tabs in the selected line(s) or document"), G_CALLBACK (mousepad_window_action_spaces_to_tabs), },
+ { "strip-trailing", NULL, N_("St_rip Trailing Spaces"), NULL, N_("Remove all the trailing spaces from the selected line(s) or document"), G_CALLBACK (mousepad_window_action_strip_trailing_spaces), },
+ { "transpose", NULL, N_("_Transpose"), "<control>T", N_("Reverse the order of something"), G_CALLBACK (mousepad_window_action_transpose), },
+ { "move-menu", NULL, N_("_Move Selection"), NULL, NULL, NULL, },
+ { "line-up", NULL, N_("Line _Up"), NULL, N_("Move the selection one line up"), G_CALLBACK (mousepad_window_action_move_line_up), },
+ { "line-down", NULL, N_("Line _Down"), NULL, N_("Move the selection one line down"), G_CALLBACK (mousepad_window_action_move_line_down), },
+ { "duplicate", NULL, N_("D_uplicate Line / Selection"), NULL, N_("Duplicate the current line or selection"), G_CALLBACK (mousepad_window_action_duplicate), },
+ { "increase-indent", GTK_STOCK_INDENT, N_("_Increase Indent"), NULL, N_("Increase the indentation of the selection or current line"), G_CALLBACK (mousepad_window_action_increase_indent), },
+ { "decrease-indent", GTK_STOCK_UNINDENT, N_("_Decrease Indent"), NULL, N_("Decrease the indentation of the selection or current line"), G_CALLBACK (mousepad_window_action_decrease_indent), },
+
+ { "document-menu", NULL, N_("_Document"), NULL, NULL, NULL, },
+ { "eol-menu", NULL, N_("Line E_nding"), NULL, NULL, NULL, },
+ { "tab-size-menu", NULL, N_("Tab _Size"), NULL, NULL, NULL, },
+ { "language-menu", NULL, N_("_Filetype"), NULL, NULL, NULL, },
+
+ { "navigation-menu", NULL, N_("_Navigation"), NULL, },
+ { "back", GTK_STOCK_GO_BACK, N_("_Previous Tab"), "<control>Page_Up", N_("Select the previous tab"), G_CALLBACK (mousepad_window_action_prev_tab), },
+ { "forward", GTK_STOCK_GO_FORWARD, N_("_Next Tab"), "<control>Page_Down", N_("Select the next tab"), G_CALLBACK (mousepad_window_action_next_tab), },
+ { "go-to", GTK_STOCK_JUMP_TO, N_("_Go to..."), "<control>G", N_("Go to a specific location in the document"), G_CALLBACK (mousepad_window_action_go_to_position), },
+
+ { "help-menu", NULL, N_("_Help"), NULL, },
+ { "contents", GTK_STOCK_HELP, N_ ("_Contents"), "F1", N_("Display the Mousepad user manual"), G_CALLBACK (mousepad_window_action_contents), },
+ { "about", GTK_STOCK_ABOUT, NULL, NULL, N_("About this application"), G_CALLBACK (mousepad_window_action_about), }
+};
+
+static const GtkToggleActionEntry toggle_action_entries[] =
+{
+ { "line-numbers", NULL, N_("Line N_umbers"), NULL, N_("Show line numbers"), G_CALLBACK (mousepad_window_action_line_numbers), FALSE, },
+ { "statusbar", NULL, N_("St_atusbar"), NULL, N_("Change the visibility of the statusbar"), G_CALLBACK (mousepad_window_action_statusbar), FALSE, },
+ { "auto-indent", NULL, N_("_Auto Indent"), NULL, N_("Auto indent a new line"), G_CALLBACK (mousepad_window_action_auto_indent), FALSE, },
+ { "insert-spaces", NULL, N_("Insert _Spaces"), NULL, N_("Insert spaces when the tab button is pressed"), G_CALLBACK (mousepad_window_action_insert_spaces), FALSE, },
+ { "word-wrap", NULL, N_("_Word Wrap"), NULL, N_("Toggle breaking lines in between words"), G_CALLBACK (mousepad_window_action_word_wrap), FALSE, },
+ { "write-bom", NULL, N_("Write Unicode _BOM"), NULL, N_("Store the byte-order mark in the file"), G_CALLBACK (mousepad_window_action_write_bom), FALSE, }
+};
+
+static const GtkRadioActionEntry radio_action_entries[] =
+{
+ { "unix", NULL, N_("Unix (_LF)"), NULL, N_("Set the line ending of the document to Unix (LF)"), MOUSEPAD_EOL_UNIX, },
+ { "mac", NULL, N_("Mac (_CR)"), NULL, N_("Set the line ending of the document to Mac (CR)"), MOUSEPAD_EOL_MAC, },
+ { "dos", NULL, N_("DOS / Windows (C_R LF)"), NULL, N_("Set the line ending of the document to DOS / Windows (CR LF)"), MOUSEPAD_EOL_DOS, }
+};
+
+
+
+/* global variables */
+static guint window_signals[LAST_SIGNAL];
+static gint lock_menu_updates = 0;
+static GSList *clipboard_history = NULL;
+static guint clipboard_history_ref_count = 0;
+
+
+
+G_DEFINE_TYPE (MousepadWindow, mousepad_window, GTK_TYPE_WINDOW);
+
+
+
+GtkWidget *
+mousepad_window_new (void)
+{
+ return g_object_new (MOUSEPAD_TYPE_WINDOW, NULL);
+}
+
+
+
+static void
+mousepad_window_class_init (MousepadWindowClass *klass)
+{
+ GObjectClass *gobject_class;
+ GtkWidgetClass *gtkwidget_class;
+
+ gobject_class = G_OBJECT_CLASS (klass);
+ gobject_class->dispose = mousepad_window_dispose;
+ gobject_class->finalize = mousepad_window_finalize;
+
+ gtkwidget_class = GTK_WIDGET_CLASS (klass);
+ gtkwidget_class->configure_event = mousepad_window_configure_event;
+
+ window_signals[NEW_WINDOW] =
+ g_signal_new (I_("new-window"),
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ window_signals[NEW_WINDOW_WITH_DOCUMENT] =
+ g_signal_new (I_("new-window-with-document"),
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL,
+ _mousepad_marshal_VOID__OBJECT_INT_INT,
+ G_TYPE_NONE, 3,
+ G_TYPE_OBJECT,
+ G_TYPE_INT, G_TYPE_INT);
+}
+
+
+
+static void
+mousepad_window_init (MousepadWindow *window)
+{
+ GtkAccelGroup *accel_group;
+ GtkWidget *menubar;
+ GtkWidget *label;
+ GtkWidget *separator;
+ GtkWidget *ebox;
+ GtkWidget *item;
+ GtkAction *action;
+ gint width, height;
+ gboolean statusbar_visible;
+
+ /* initialize stuff */
+ window->save_geometry_timer_id = 0;
+ window->update_recent_menu_id = 0;
+ window->update_go_menu_id = 0;
+ window->gomenu_merge_id = 0;
+ window->recent_merge_id = 0;
+ window->search_bar = NULL;
+ window->statusbar = NULL;
+ window->replace_dialog = NULL;
+ window->active = NULL;
+ window->recent_manager = NULL;
+
+ /* setup window */
+ gtk_window_set_icon_name (GTK_WINDOW (window), "accessories-text-editor");
+
+ /* increase clipboard history ref count */
+ clipboard_history_ref_count++;
+
+ /* add the preferences to the window */
+ window->preferences = mousepad_preferences_get ();
+
+ /* signal for handling the window delete event */
+ g_signal_connect (G_OBJECT (window), "delete-event", G_CALLBACK (mousepad_window_delete_event), NULL);
+
+ /* allocate a closure for the menu_item_selected() callback */
+ window->menu_item_selected_closure = g_cclosure_new_object (G_CALLBACK (mousepad_window_menu_item_selected), G_OBJECT (window));
+ g_closure_ref (window->menu_item_selected_closure);
+ g_closure_sink (window->menu_item_selected_closure);
+
+ /* allocate a closure for the menu_item_deselected() callback */
+ window->menu_item_deselected_closure = g_cclosure_new_object (G_CALLBACK (mousepad_window_menu_item_deselected), G_OBJECT (window));
+ g_closure_ref (window->menu_item_deselected_closure);
+ g_closure_sink (window->menu_item_deselected_closure);
+
+ /* read settings from the preferences */
+ g_object_get (G_OBJECT (window->preferences),
+ "window-width", &width,
+ "window-height", &height,
+ "window-statusbar-visible", &statusbar_visible,
+ NULL);
+
+ /* set the default window size */
+ gtk_window_set_default_size (GTK_WINDOW (window), width, height);
+
+ /* the action group for this window */
+ window->action_group = gtk_action_group_new ("MousepadWindow");
+ gtk_action_group_set_translation_domain (window->action_group, GETTEXT_PACKAGE);
+ gtk_action_group_add_actions (window->action_group, action_entries, G_N_ELEMENTS (action_entries), GTK_WIDGET (window));
+ gtk_action_group_add_toggle_actions (window->action_group, toggle_action_entries, G_N_ELEMENTS (toggle_action_entries), GTK_WIDGET (window));
+ gtk_action_group_add_radio_actions (window->action_group, radio_action_entries, G_N_ELEMENTS (radio_action_entries), -1, G_CALLBACK (mousepad_window_action_line_ending), GTK_WIDGET (window));
+
+ /* create the ui manager and connect proxy signals for the statusbar */
+ window->ui_manager = gtk_ui_manager_new ();
+ g_signal_connect (G_OBJECT (window->ui_manager), "connect-proxy", G_CALLBACK (mousepad_window_connect_proxy), window);
+ g_signal_connect (G_OBJECT (window->ui_manager), "disconnect-proxy", G_CALLBACK (mousepad_window_disconnect_proxy), window);
+ gtk_ui_manager_insert_action_group (window->ui_manager, window->action_group, 0);
+ gtk_ui_manager_add_ui_from_string (window->ui_manager, mousepad_window_ui, mousepad_window_ui_length, NULL);
+
+ /* build the templates menu when the item is shown for the first time */
+ /* from here we also trigger the idle build of the recent menu */
+ item = gtk_ui_manager_get_widget (window->ui_manager, "/main-menu/file-menu/template-menu");
+ g_signal_connect (G_OBJECT (item), "map", G_CALLBACK (mousepad_window_menu_templates), window);
+
+ /* add tab size menu */
+ mousepad_window_menu_tab_sizes (window);
+
+ /* add color schemes menu */
+ mousepad_window_menu_color_schemes (window);
+
+ /* add languages/filetypes menu */
+ mousepad_window_menu_languages (window);
+
+ /* set accel group for the window */
+ accel_group = gtk_ui_manager_get_accel_group (window->ui_manager);
+ gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
+
+ /* create the main table */
+ window->box = gtk_vbox_new (FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (window), window->box);
+ gtk_widget_show (window->box);
+
+ menubar = gtk_ui_manager_get_widget (window->ui_manager, "/main-menu");
+ gtk_box_pack_start (GTK_BOX (window->box), menubar, FALSE, FALSE, 0);
+ gtk_widget_show (menubar);
+
+ /* check if we need to add the root warning */
+ if (G_UNLIKELY (geteuid () == 0))
+ {
+ /* install default settings for the root warning text box */
+ gtk_rc_parse_string ("style\"mousepad-window-root-style\"\n"
+ "{\n"
+ "bg[NORMAL]=\"#b4254b\"\n"
+ "fg[NORMAL]=\"#fefefe\"\n"
+ "}\n"
+ "widget\"MousepadWindow.*.root-warning\"style\"mousepad-window-root-style\"\n"
+ "widget\"MousepadWindow.*.root-warning.GtkLabel\"style\"mousepad-window-root-style\"\n");
+
+ /* add the box for the root warning */
+ ebox = gtk_event_box_new ();
+ gtk_widget_set_name (ebox, "root-warning");
+ gtk_box_pack_start (GTK_BOX (window->box), ebox, FALSE, FALSE, 0);
+ gtk_widget_show (ebox);
+
+ /* add the label with the root warning */
+ label = gtk_label_new (_("Warning, you are using the root account, you may harm your system."));
+ gtk_misc_set_padding (GTK_MISC (label), 6, 3);
+ gtk_container_add (GTK_CONTAINER (ebox), label);
+ gtk_widget_show (label);
+
+ separator = gtk_hseparator_new ();
+ gtk_box_pack_start (GTK_BOX (window->box), separator, FALSE, FALSE, 0);
+ gtk_widget_show (separator);
+ }
+
+ /* create the notebook */
+ window->notebook = g_object_new (GTK_TYPE_NOTEBOOK,
+ "homogeneous", FALSE,
+ "scrollable", TRUE,
+ "show-border", FALSE,
+ "show-tabs", FALSE,
+ "tab-hborder", 0,
+ "tab-vborder", 0,
+ NULL);
+
+ /* set the group id */
+#if GTK_CHECK_VERSION (2,12,0)
+ gtk_notebook_set_group (GTK_NOTEBOOK (window->notebook), (gpointer) NOTEBOOK_GROUP);
+#else
+ gtk_notebook_set_group_id (GTK_NOTEBOOK (window->notebook), 1337);
+#endif
+
+ /* connect signals to the notebooks */
+ g_signal_connect (G_OBJECT (window->notebook), "switch-page", G_CALLBACK (mousepad_window_notebook_switch_page), window);
+ g_signal_connect (G_OBJECT (window->notebook), "page-reordered", G_CALLBACK (mousepad_window_notebook_reordered), window);
+ g_signal_connect (G_OBJECT (window->notebook), "page-added", G_CALLBACK (mousepad_window_notebook_added), window);
+ g_signal_connect (G_OBJECT (window->notebook), "page-removed", G_CALLBACK (mousepad_window_notebook_removed), window);
+ g_signal_connect (G_OBJECT (window->notebook), "button-press-event", G_CALLBACK (mousepad_window_notebook_button_press_event), window);
+ g_signal_connect (G_OBJECT (window->notebook), "button-release-event", G_CALLBACK (mousepad_window_notebook_button_release_event), window);
+#if GTK_CHECK_VERSION (2,12,0)
+ g_signal_connect (G_OBJECT (window->notebook), "create-window", G_CALLBACK (mousepad_window_notebook_create_window), window);
+#endif
+
+ /* append and show the notebook */
+ gtk_box_pack_start (GTK_BOX (window->box), window->notebook, TRUE, TRUE, PADDING);
+ gtk_widget_show (window->notebook);
+
+ /* check if we should display the statusbar by default */
+ action = gtk_action_group_get_action (window->action_group, "statusbar");
+ gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), statusbar_visible);
+
+ /* allow drops in the window */
+ gtk_drag_dest_set (GTK_WIDGET (window), GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP, drop_targets, G_N_ELEMENTS (drop_targets), GDK_ACTION_COPY | GDK_ACTION_MOVE);
+ g_signal_connect (G_OBJECT (window), "drag-data-received", G_CALLBACK (mousepad_window_drag_data_received), window);
+
+}
+
+
+
+static void
+mousepad_window_dispose (GObject *object)
+{
+ MousepadWindow *window = MOUSEPAD_WINDOW (object);
+
+ /* disconnect recent manager signal */
+ if (G_LIKELY (window->recent_manager))
+ g_signal_handlers_disconnect_by_func (G_OBJECT (window->recent_manager), mousepad_window_recent_menu, window);
+
+ /* destroy the save geometry timer source */
+ if (G_UNLIKELY (window->save_geometry_timer_id != 0))
+ g_source_remove (window->save_geometry_timer_id);
+
+ (*G_OBJECT_CLASS (mousepad_window_parent_class)->dispose) (object);
+}
+
+
+
+static void
+mousepad_window_finalize (GObject *object)
+{
+ MousepadWindow *window = MOUSEPAD_WINDOW (object);
+
+ /* decrease history clipboard ref count */
+ clipboard_history_ref_count--;
+
+ /* cancel a scheduled recent menu update */
+ if (G_UNLIKELY (window->update_recent_menu_id != 0))
+ g_source_remove (window->update_recent_menu_id);
+
+ /* cancel a scheduled go menu update */
+ if (G_UNLIKELY (window->update_go_menu_id != 0))
+ g_source_remove (window->update_go_menu_id);
+
+ /* drop our references on the menu_item_selected()/menu_item_deselected() closures */
+ g_closure_unref (window->menu_item_deselected_closure);
+ g_closure_unref (window->menu_item_selected_closure);
+
+ /* release the ui manager */
+ g_signal_handlers_disconnect_matched (G_OBJECT (window->ui_manager), G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, window);
+ g_object_unref (G_OBJECT (window->ui_manager));
+
+ /* release the action group */
+ g_object_unref (G_OBJECT (window->action_group));
+
+ /* release the preferences reference */
+ g_object_unref (G_OBJECT (window->preferences));
+
+ /* free clipboard history if needed */
+ if (clipboard_history_ref_count == 0 && clipboard_history != NULL)
+ {
+ g_slist_foreach (clipboard_history, (GFunc) g_free, NULL);
+ g_slist_free (clipboard_history);
+ }
+
+ (*G_OBJECT_CLASS (mousepad_window_parent_class)->finalize) (object);
+}
+
+
+
+static gboolean
+mousepad_window_configure_event (GtkWidget *widget,
+ GdkEventConfigure *event)
+{
+ MousepadWindow *window = MOUSEPAD_WINDOW (widget);
+
+ /* check if we have a new dimension here */
+ if (widget->allocation.width != event->width || widget->allocation.height != event->height)
+ {
+ /* drop any previous timer source */
+ if (window->save_geometry_timer_id > 0)
+ g_source_remove (window->save_geometry_timer_id);
+
+ /* check if we should schedule another save timer */
+ if (GTK_WIDGET_VISIBLE (widget))
+ {
+ /* save the geometry one second after the last configure event */
+ window->save_geometry_timer_id = g_timeout_add_full (G_PRIORITY_LOW, 1000, mousepad_window_save_geometry_timer,
+ window, mousepad_window_save_geometry_timer_destroy);
+ }
+ }
+
+ /* let gtk+ handle the configure event */
+ return (*GTK_WIDGET_CLASS (mousepad_window_parent_class)->configure_event) (widget, event);
+}
+
+
+
+/**
+ * Statusbar Tooltip Functions
+ **/
+static void
+mousepad_window_connect_proxy (GtkUIManager *manager,
+ GtkAction *action,
+ GtkWidget *proxy,
+ MousepadWindow *window)
+{
+ mousepad_return_if_fail (GTK_IS_ACTION (action));
+ mousepad_return_if_fail (GTK_IS_MENU_ITEM (proxy));
+ mousepad_return_if_fail (GTK_IS_UI_MANAGER (manager));
+
+ /* we want to get informed when the user hovers a menu item */
+ g_signal_connect_closure (G_OBJECT (proxy), "select", window->menu_item_selected_closure, FALSE);
+ g_signal_connect_closure (G_OBJECT (proxy), "deselect", window->menu_item_deselected_closure, FALSE);
+}
+
+
+
+static void
+mousepad_window_disconnect_proxy (GtkUIManager *manager,
+ GtkAction *action,
+ GtkWidget *proxy,
+ MousepadWindow *window)
+{
+ mousepad_return_if_fail (GTK_IS_ACTION (action));
+ mousepad_return_if_fail (GTK_IS_MENU_ITEM (proxy));
+ mousepad_return_if_fail (GTK_IS_UI_MANAGER (manager));
+
+ /* disconnect the signal from mousepad_window_connect_proxy() */
+ g_signal_handlers_disconnect_matched (G_OBJECT (proxy), G_SIGNAL_MATCH_CLOSURE, 0, 0, window->menu_item_selected_closure, NULL, NULL);
+ g_signal_handlers_disconnect_matched (G_OBJECT (proxy), G_SIGNAL_MATCH_CLOSURE, 0, 0, window->menu_item_deselected_closure, NULL, NULL);
+}
+
+
+
+static void
+mousepad_window_menu_item_selected (GtkWidget *menu_item,
+ MousepadWindow *window)
+{
+ GtkAction *action;
+ gchar *tooltip;
+ gint id;
+
+ /* we can only display tooltips if we have a statusbar */
+ if (G_LIKELY (window->statusbar != NULL))
+ {
+ /* get the action from the menu item */
+#if GTK_CHECK_VERSION (2, 16, 0)
+ action = gtk_activatable_get_related_action (GTK_ACTIVATABLE (menu_item));
+#else
+ action = gtk_widget_get_action (menu_item);
+#endif
+ if (G_LIKELY (action))
+ {
+ /* read the tooltip from the action, if there is one */
+ g_object_get (G_OBJECT (action), "tooltip", &tooltip, NULL);
+
+ if (G_LIKELY (tooltip != NULL))
+ {
+ /* show the tooltip */
+ id = gtk_statusbar_get_context_id (GTK_STATUSBAR (window->statusbar), "tooltip");
+ gtk_statusbar_push (GTK_STATUSBAR (window->statusbar), id, tooltip);
+
+ /* cleanup */
+ g_free (tooltip);
+ }
+ }
+ }
+}
+
+
+
+static void
+mousepad_window_menu_item_deselected (GtkWidget *menu_item,
+ MousepadWindow *window)
+{
+ gint id;
+
+ /* we can only undisplay tooltips if we have a statusbar */
+ if (G_LIKELY (window->statusbar != NULL))
+ {
+ /* drop the last tooltip from the statusbar */
+ id = gtk_statusbar_get_context_id (GTK_STATUSBAR (window->statusbar), "tooltip");
+ gtk_statusbar_pop (GTK_STATUSBAR (window->statusbar), id);
+ }
+}
+
+
+
+/**
+ * Save Geometry Functions
+ **/
+static gboolean
+mousepad_window_save_geometry_timer (gpointer user_data)
+{
+ GdkWindowState state;
+ MousepadWindow *window = MOUSEPAD_WINDOW (user_data);
+ gboolean remember_geometry;
+ gint width;
+ gint height;
+
+ GDK_THREADS_ENTER ();
+
+ /* check if we should remember the window geometry */
+ g_object_get (G_OBJECT (window->preferences), "misc-remember-geometry", &remember_geometry, NULL);
+ if (G_LIKELY (remember_geometry))
+ {
+ /* check if the window is still visible */
+ if (GTK_WIDGET_VISIBLE (window))
+ {
+ /* determine the current state of the window */
+ state = gdk_window_get_state (GTK_WIDGET (window)->window);
+
+ /* don't save geometry for maximized or fullscreen windows */
+ if ((state & (GDK_WINDOW_STATE_MAXIMIZED | GDK_WINDOW_STATE_FULLSCREEN)) == 0)
+ {
+ /* determine the current width/height of the window... */
+ gtk_window_get_size (GTK_WINDOW (window), &width, &height);
+
+ /* ...and remember them as default for new windows */
+ g_object_set (G_OBJECT (window->preferences),
+ "window-width", width,
+ "window-height", height, NULL);
+ }
+ }
+ }
+
+ GDK_THREADS_LEAVE ();
+
+ return FALSE;
+}
+
+
+
+static void
+mousepad_window_save_geometry_timer_destroy (gpointer user_data)
+{
+ MOUSEPAD_WINDOW (user_data)->save_geometry_timer_id = 0;
+}
+
+
+
+/**
+ * Mousepad Window Functions
+ **/
+static gboolean
+mousepad_window_open_file (MousepadWindow *window,
+ const gchar *filename,
+ MousepadEncoding encoding)
+{
+ MousepadDocument *document;
+ GError *error = NULL;
+ gint result;
+ gint npages = 0, i;
+ gint response;
+ const gchar *charset;
+ const gchar *opened_filename;
+ GtkWidget *dialog;
+ gboolean encoding_from_recent = FALSE;
+ gchar *uri;
+ GtkRecentInfo *info;
+
+ mousepad_return_val_if_fail (MOUSEPAD_IS_WINDOW (window), FALSE);
+ mousepad_return_val_if_fail (filename != NULL && *filename != '\0', FALSE);
+
+ /* check if the file is already openend */
+ npages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (window->notebook));
+ for (i = 0; i < npages; i++)
+ {
+ document = MOUSEPAD_DOCUMENT (gtk_notebook_get_nth_page (GTK_NOTEBOOK (window->notebook), i));
+
+ /* debug check */
+ mousepad_return_val_if_fail (MOUSEPAD_IS_DOCUMENT (document), FALSE);
+
+ if (G_LIKELY (document))
+ {
+ /* get the filename */
+ opened_filename = mousepad_file_get_filename (MOUSEPAD_DOCUMENT (document)->file);
+
+ /* see if the file is already opened */
+ if (opened_filename && strcmp (filename, opened_filename) == 0)
+ {
+ /* switch to the tab */
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (window->notebook), i);
+
+ /* and we're done */
+ return TRUE;
+ }
+ }
+ }
+
+ /* new document */
+ document = mousepad_document_new ();
+
+ /* make sure it's not a floating object */
+ g_object_ref_sink (G_OBJECT (document));
+
+ /* set the filename */
+ mousepad_file_set_filename (document->file, filename);
+
+ /* set the passed encoding */
+ mousepad_file_set_encoding (document->file, encoding);
+
+ retry:
+
+ /* lock the undo manager */
+ gtk_source_buffer_begin_not_undoable_action (GTK_SOURCE_BUFFER (document->buffer));
+
+ /* read the content into the buffer */
+ result = mousepad_file_open (document->file, NULL, &error);
+
+ /* release the lock */
+ gtk_source_buffer_end_not_undoable_action (GTK_SOURCE_BUFFER (document->buffer));
+
+ switch (result)
+ {
+ case 0:
+ /* add the document to the window */
+ mousepad_window_add (window, document);
+
+ /* insert in the recent history */
+ mousepad_window_recent_add (window, document->file);
+ break;
+
+ case ERROR_CONVERTING_FAILED:
+ case ERROR_NOT_UTF8_VALID:
+ /* clear the error */
+ g_clear_error (&error);
+
+ /* try to lookup the encoding from the recent history */
+ if (encoding_from_recent == FALSE)
+ {
+ /* we only try this once */
+ encoding_from_recent = TRUE;
+
+ /* build uri */
+ uri = g_filename_to_uri (filename, NULL, NULL);
+ if (G_LIKELY (uri))
+ {
+ /* try to lookup the recent item */
+ info = gtk_recent_manager_lookup_item (window->recent_manager, uri, NULL);
+
+ /* cleanup */
+ g_free (uri);
+
+ if (info)
+ {
+ /* try to find the encoding */
+ charset = mousepad_window_recent_get_charset (info);
+
+ encoding = mousepad_encoding_find (charset);
+
+ /* set the new encoding */
+ mousepad_file_set_encoding (document->file, encoding);
+
+ /* release */
+ gtk_recent_info_unref (info);
+
+ /* try to open again with the last used encoding */
+ if (G_LIKELY (encoding))
+ goto retry;
+ }
+ }
+ }
+
+ /* run the encoding dialog */
+ dialog = mousepad_encoding_dialog_new (GTK_WINDOW (window), document->file);
+
+ /* run the dialog */
+ response = gtk_dialog_run (GTK_DIALOG (dialog));
+
+ if (response == GTK_RESPONSE_OK)
+ {
+ /* set the new encoding */
+ encoding = mousepad_encoding_dialog_get_encoding (MOUSEPAD_ENCODING_DIALOG (dialog));
+
+ /* set encoding */
+ mousepad_file_set_encoding (document->file, encoding);
+ }
+
+ /* destroy the dialog */
+ gtk_widget_destroy (dialog);
+
+ /* handle */
+ if (response == GTK_RESPONSE_OK)
+ goto retry;
+
+ default:
+ /* something went wrong, release the document */
+ g_object_unref (G_OBJECT (document));
+
+ if (G_LIKELY (error))
+ {
+ /* show the warning */
+ mousepad_dialogs_show_error (GTK_WINDOW (window), error, _("Failed to open the document"));
+
+ /* cleanup */
+ g_error_free (error);
+ }
+
+ break;
+ }
+
+ return (result == 0);
+}
+
+
+
+gboolean
+mousepad_window_open_files (MousepadWindow *window,
+ const gchar *working_directory,
+ gchar **filenames)
+{
+ guint n;
+ gchar *filename;
+
+ mousepad_return_val_if_fail (MOUSEPAD_IS_WINDOW (window), FALSE);
+ mousepad_return_val_if_fail (working_directory != NULL, FALSE);
+ mousepad_return_val_if_fail (filenames != NULL, FALSE);
+ mousepad_return_val_if_fail (*filenames != NULL, FALSE);
+
+ /* block menu updates */
+ lock_menu_updates++;
+
+ /* walk through all the filenames */
+ for (n = 0; filenames[n] != NULL; ++n)
+ {
+ /* check if the filename looks like an uri */
+ if (strncmp (filenames[n], "file:", 5) == 0)
+ {
+ /* convert the uri to an absolute filename */
+ filename = g_filename_from_uri (filenames[n], NULL, NULL);
+ }
+ else if (g_path_is_absolute (filenames[n]) == FALSE)
+ {
+ /* create an absolute file */
+ filename = g_build_filename (working_directory, filenames[n], NULL);
+ }
+ else
+ {
+ /* looks like a valid filename */
+ filename = NULL;
+ }
+
+ /* open a new tab with the file */
+ mousepad_window_open_file (window, filename ? filename : filenames[n], MOUSEPAD_ENCODING_UTF_8);
+
+ /* cleanup */
+ g_free (filename);
+ }
+
+ /* allow menu updates again */
+ lock_menu_updates--;
+
+ /* check if the window contains tabs */
+ if (gtk_notebook_get_n_pages (GTK_NOTEBOOK (window->notebook)) == 0)
+ return FALSE;
+
+ /* update the menus */
+ mousepad_window_recent_menu (window);
+ mousepad_window_update_gomenu (window);
+
+ return TRUE;
+}
+
+
+
+void
+mousepad_window_add (MousepadWindow *window,
+ MousepadDocument *document)
+{
+ GtkWidget *label;
+ gint page;
+ MousepadDocument *prev_active = window->active;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (document));
+ mousepad_return_if_fail (GTK_IS_NOTEBOOK (window->notebook));
+
+ /* create the tab label */
+ label = mousepad_document_get_tab_label (document);
+
+ /* get active page */
+ page = gtk_notebook_get_current_page (GTK_NOTEBOOK (window->notebook));
+
+ /* insert the page right of the active tab */
+ page = gtk_notebook_insert_page (GTK_NOTEBOOK (window->notebook), GTK_WIDGET (document), label, page + 1);
+
+ /* allow tab reordering and detaching */
+ gtk_notebook_set_tab_reorderable (GTK_NOTEBOOK (window->notebook), GTK_WIDGET (document), TRUE);
+ gtk_notebook_set_tab_detachable (GTK_NOTEBOOK (window->notebook), GTK_WIDGET (document), TRUE);
+
+ /* show the document */
+ gtk_widget_show (GTK_WIDGET (document));
+
+ /* don't bother about this when there was no previous active page (startup) */
+ if (G_LIKELY (prev_active != NULL))
+ {
+ /* switch to the new tab */
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (window->notebook), page);
+
+ /* destroy the previous tab if it was not modified, untitled and the new tab is not untitled */
+ if (gtk_text_buffer_get_modified (prev_active->buffer) == FALSE
+ && mousepad_file_get_filename (prev_active->file) == NULL
+ && mousepad_file_get_filename (document->file) != NULL)
+ gtk_widget_destroy (GTK_WIDGET (prev_active));
+ }
+
+ /* make sure the textview is focused in the new document */
+ mousepad_document_focus_textview (document);
+}
+
+
+
+static gboolean
+mousepad_window_close_document (MousepadWindow *window,
+ MousepadDocument *document)
+{
+ gboolean succeed = FALSE;
+ gint response;
+ gboolean readonly;
+
+ mousepad_return_val_if_fail (MOUSEPAD_IS_WINDOW (window), FALSE);
+ mousepad_return_val_if_fail (MOUSEPAD_IS_DOCUMENT (document), FALSE);
+
+ /* check if the document has been modified */
+ if (gtk_text_buffer_get_modified (document->buffer))
+ {
+ /* whether the file is readonly */
+ readonly = mousepad_file_get_read_only (document->file);
+
+ /* run save changes dialog */
+ response = mousepad_dialogs_save_changes (GTK_WINDOW (window), readonly);
+
+ switch (response)
+ {
+ case MOUSEPAD_RESPONSE_DONT_SAVE:
+ /* don't save, only destroy the document */
+ succeed = TRUE;
+ break;
+
+ case MOUSEPAD_RESPONSE_CANCEL:
+ /* do nothing */
+ break;
+
+ case MOUSEPAD_RESPONSE_SAVE:
+ succeed = mousepad_window_action_save (NULL, window);
+ break;
+
+ case MOUSEPAD_RESPONSE_SAVE_AS:
+ succeed = mousepad_window_action_save_as (NULL, window);
+ break;
+ }
+ }
+ else
+ {
+ /* no changes in the document, safe to destroy it */
+ succeed = TRUE;
+ }
+
+ /* destroy the document */
+ if (succeed)
+ gtk_widget_destroy (GTK_WIDGET (document));
+
+ return succeed;
+}
+
+
+
+static void
+mousepad_window_set_title (MousepadWindow *window)
+{
+ gchar *string;
+ const gchar *title;
+ gboolean show_full_path;
+ MousepadDocument *document = window->active;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (document));
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+
+ /* whether to show the full path */
+ g_object_get (G_OBJECT (window->preferences), "misc-path-in-title", &show_full_path, NULL);
+
+ /* name we display in the title */
+ if (G_UNLIKELY (show_full_path && mousepad_document_get_filename (document)))
+ title = mousepad_document_get_filename (document);
+ else
+ title = mousepad_document_get_basename (document);
+
+ /* build the title */
+ if (G_UNLIKELY (mousepad_file_get_read_only (document->file)))
+ string = g_strdup_printf ("%s [%s] - %s", title, _("Read Only"), PACKAGE_NAME);
+ else
+ string = g_strdup_printf ("%s%s - %s", gtk_text_buffer_get_modified (document->buffer) ? "*" : "", title, PACKAGE_NAME);
+
+ /* set the window title */
+ gtk_window_set_title (GTK_WINDOW (window), string);
+
+ /* cleanup */
+ g_free (string);
+}
+
+
+
+static void
+mousepad_window_statusbar_filetype_toggled (GtkCheckMenuItem *item,
+ MousepadWindow *window)
+{
+ const gchar *language_id;
+ GtkSourceLanguage *language;
+ GtkSourceLanguageManager *manager;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+
+ manager = gtk_source_language_manager_get_default ();
+ language_id = g_object_get_data (G_OBJECT (item), "language_id");
+
+ /* check if None was selected */
+ if (!language_id || g_strcmp0 (language_id, "none") == 0)
+ {
+ gtk_source_buffer_set_language (GTK_SOURCE_BUFFER (window->active->buffer), NULL);
+ gtk_source_buffer_set_highlight_syntax (GTK_SOURCE_BUFFER (window->active->buffer), FALSE);
+ return;
+ }
+
+ /* set to a non-None language */
+ language = gtk_source_language_manager_get_language (manager, language_id);
+ gtk_source_buffer_set_highlight_syntax (GTK_SOURCE_BUFFER (window->active->buffer), TRUE);
+ gtk_source_buffer_set_language (GTK_SOURCE_BUFFER (window->active->buffer), language);
+}
+
+
+
+static void
+mousepad_window_populate_statusbar_popup (MousepadWindow *window,
+ GtkMenu *menu,
+ MousepadStatusbar *statusbar)
+{
+ GSList *group = NULL;
+ GSList *sections, *s_iter;
+ GSList *languages, *l_iter;
+ GtkWidget *item;
+ GtkWidget *submenu;
+ GtkSourceLanguage *active;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+
+ active = gtk_source_buffer_get_language (GTK_SOURCE_BUFFER (window->active->buffer));
+
+ item = gtk_radio_menu_item_new_with_label (group, _("None"));
+ group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (item));
+ g_object_set_data (G_OBJECT (item), "language_id", "none");
+ g_signal_connect (item, "toggled", G_CALLBACK (mousepad_window_statusbar_filetype_toggled), window);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+ gtk_widget_show (item);
+
+ if (!active)
+ gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), TRUE);
+
+ item = gtk_separator_menu_item_new ();
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+ gtk_widget_show (item);
+
+ sections = mousepad_util_language_sections_get_sorted ();
+
+ for (s_iter = sections ; s_iter != NULL; s_iter = g_slist_next (s_iter))
+ {
+ item = gtk_menu_item_new_with_label (s_iter->data);
+ gtk_widget_show (item);
+ submenu = gtk_menu_new ();
+ gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu);
+ gtk_widget_show (submenu);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+
+ languages = mousepad_util_languages_get_sorted_for_section (s_iter->data);
+ for (l_iter = languages; l_iter != NULL; l_iter = g_slist_next (l_iter))
+ {
+ item = gtk_radio_menu_item_new_with_label (group, gtk_source_language_get_name (l_iter->data));
+ group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (item));
+ g_object_set_data (G_OBJECT (item), "language_id", (gpointer) gtk_source_language_get_id (l_iter->data));
+ g_signal_connect (item, "toggled", G_CALLBACK (mousepad_window_statusbar_filetype_toggled), window);
+ gtk_menu_shell_append (GTK_MENU_SHELL (submenu), item);
+ gtk_widget_show (item);
+
+ if (active == l_iter->data)
+ gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), TRUE);
+ }
+ }
+}
+
+
+
+/**
+ * Notebook Signal Functions
+ **/
+static void
+mousepad_window_notebook_switch_page (GtkNotebook *notebook,
+ GtkNotebookPage *page,
+ guint page_num,
+ MousepadWindow *window)
+{
+ MousepadDocument *document;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (GTK_IS_NOTEBOOK (notebook));
+
+ /* get the new active document */
+ document = MOUSEPAD_DOCUMENT (gtk_notebook_get_nth_page (notebook, page_num));
+
+ /* only update when really changed */
+ if (G_LIKELY (window->active != document))
+ {
+ /* set new active document */
+ window->active = document;
+
+ /* set the window title */
+ mousepad_window_set_title (window);
+
+ /* update the menu actions */
+ mousepad_window_update_actions (window);
+
+ /* update the statusbar */
+ mousepad_document_send_signals (window->active);
+ }
+}
+
+
+
+static void
+mousepad_window_notebook_reordered (GtkNotebook *notebook,
+ GtkWidget *page,
+ guint page_num,
+ MousepadWindow *window)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (page));
+
+ /* update the go menu */
+ mousepad_window_update_gomenu (window);
+}
+
+
+
+static void
+mousepad_window_notebook_added (GtkNotebook *notebook,
+ GtkWidget *page,
+ guint page_num,
+ MousepadWindow *window)
+{
+ MousepadDocument *document = MOUSEPAD_DOCUMENT (page);
+ gboolean always_show_tabs;
+ gint npages;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (document));
+
+ /* connect signals to the document for this window */
+ g_signal_connect (G_OBJECT (page), "close-tab", G_CALLBACK (mousepad_window_button_close_tab), window);
+ g_signal_connect (G_OBJECT (page), "cursor-changed", G_CALLBACK (mousepad_window_cursor_changed), window);
+ g_signal_connect (G_OBJECT (page), "selection-changed", G_CALLBACK (mousepad_window_selection_changed), window);
+ g_signal_connect (G_OBJECT (page), "overwrite-changed", G_CALLBACK (mousepad_window_overwrite_changed), window);
+ g_signal_connect (G_OBJECT (page), "language-changed", G_CALLBACK (mousepad_window_language_changed), window);
+ g_signal_connect (G_OBJECT (page), "drag-data-received", G_CALLBACK (mousepad_window_drag_data_received), window);
+ g_signal_connect_swapped (G_OBJECT (document->buffer), "notify::can-undo", G_CALLBACK (mousepad_window_can_undo), window);
+ g_signal_connect_swapped (G_OBJECT (document->buffer), "notify::can-redo", G_CALLBACK (mousepad_window_can_redo), window);
+ g_signal_connect_swapped (G_OBJECT (document->buffer), "modified-changed", G_CALLBACK (mousepad_window_modified_changed), window);
+ g_signal_connect (G_OBJECT (document->textview), "populate-popup", G_CALLBACK (mousepad_window_menu_textview_popup), window);
+
+ /* get the number of pages */
+ npages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (window->notebook));
+
+ /* check tabs should always be visible */
+ g_object_get (G_OBJECT (window->preferences), "misc-always-show-tabs", &always_show_tabs, NULL);
+
+ /* change the visibility of the tabs accordingly */
+ gtk_notebook_set_show_tabs (GTK_NOTEBOOK (window->notebook), always_show_tabs || (npages > 1));
+
+ /* update the go menu */
+ mousepad_window_update_gomenu (window);
+}
+
+
+
+static void
+mousepad_window_notebook_removed (GtkNotebook *notebook,
+ GtkWidget *page,
+ guint page_num,
+ MousepadWindow *window)
+{
+ gboolean always_show_tabs;
+ gint npages;
+ MousepadDocument *document = MOUSEPAD_DOCUMENT (page);
+
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (document));
+ mousepad_return_if_fail (GTK_IS_NOTEBOOK (notebook));
+
+ /* disconnect the old document signals */
+ g_signal_handlers_disconnect_by_func (G_OBJECT (page), mousepad_window_button_close_tab, window);
+ g_signal_handlers_disconnect_by_func (G_OBJECT (page), mousepad_window_cursor_changed, window);
+ g_signal_handlers_disconnect_by_func (G_OBJECT (page), mousepad_window_selection_changed, window);
+ g_signal_handlers_disconnect_by_func (G_OBJECT (page), mousepad_window_overwrite_changed, window);
+ g_signal_handlers_disconnect_by_func (G_OBJECT (page), mousepad_window_language_changed, window);
+ g_signal_handlers_disconnect_by_func (G_OBJECT (page), mousepad_window_drag_data_received, window);
+ g_signal_handlers_disconnect_by_func (G_OBJECT (document->buffer), mousepad_window_can_undo, window);
+ g_signal_handlers_disconnect_by_func (G_OBJECT (document->buffer), mousepad_window_can_redo, window);
+ g_signal_handlers_disconnect_by_func (G_OBJECT (document->buffer), mousepad_window_modified_changed, window);
+ g_signal_handlers_disconnect_by_func (G_OBJECT (document->textview), mousepad_window_menu_textview_popup, window);
+
+ /* unset the go menu item (part of the old window) */
+ mousepad_object_set_data (G_OBJECT (page), "navigation-menu-action", NULL);
+
+ /* get the number of pages in this notebook */
+ npages = gtk_notebook_get_n_pages (notebook);
+
+ /* update the window */
+ if (npages == 0)
+ {
+ /* window contains no tabs, destroy it */
+ gtk_widget_destroy (GTK_WIDGET (window));
+ }
+ else
+ {
+ /* check tabs should always be visible */
+ g_object_get (G_OBJECT (window->preferences), "misc-always-show-tabs", &always_show_tabs, NULL);
+
+ /* change the visibility of the tabs accordingly */
+ gtk_notebook_set_show_tabs (GTK_NOTEBOOK (window->notebook), always_show_tabs || (npages > 1));
+
+ /* update the go menu */
+ mousepad_window_update_gomenu (window);
+
+ /* update action entries */
+ mousepad_window_update_actions (window);
+ }
+}
+
+
+
+static void
+mousepad_window_notebook_menu_position (GtkMenu *menu,
+ gint *x,
+ gint *y,
+ gboolean *push_in,
+ gpointer user_data)
+{
+ GtkWidget *widget = GTK_WIDGET (user_data);
+
+ gdk_window_get_origin (widget->window, x, y);
+
+ *x += widget->allocation.x;
+ *y += widget->allocation.y + widget->allocation.height;
+
+ *push_in = TRUE;
+}
+
+
+
+static gboolean
+mousepad_window_notebook_button_press_event (GtkNotebook *notebook,
+ GdkEventButton *event,
+ MousepadWindow *window)
+{
+ GtkWidget *page, *label;
+ GtkWidget *menu;
+ guint page_num = 0;
+ gint x_root;
+
+ mousepad_return_val_if_fail (MOUSEPAD_IS_WINDOW (window), FALSE);
+
+ if (event->type == GDK_BUTTON_PRESS && (event->button == 3 || event->button == 2))
+ {
+ /* walk through the tabs and look for the tab under the cursor */
+ while ((page = gtk_notebook_get_nth_page (notebook, page_num)) != NULL)
+ {
+ label = gtk_notebook_get_tab_label (notebook, page);
+
+ /* get the origin of the label */
+ gdk_window_get_origin (label->window, &x_root, NULL);
+ x_root = x_root + label->allocation.x;
+
+ /* check if the cursor is inside this label */
+ if (event->x_root >= x_root && event->x_root <= (x_root + label->allocation.width))
+ {
+ /* switch to this tab */
+ gtk_notebook_set_current_page (notebook, page_num);
+
+ /* handle the button action */
+ if (event->button == 3)
+ {
+ /* get the menu */
+ menu = gtk_ui_manager_get_widget (window->ui_manager, "/tab-menu");
+
+ /* show it */
+ gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
+ mousepad_window_notebook_menu_position, label,
+ event->button, event->time);
+ }
+ else if (event->button == 2)
+ {
+ /* close the document */
+ mousepad_window_action_close (NULL, window);
+ }
+
+ /* we succeed */
+ return TRUE;
+ }
+
+ /* try the next tab */
+ ++page_num;
+ }
+ }
+ else if (event->type == GDK_2BUTTON_PRESS && event->button == 1)
+ {
+ /* check if the event window is the notebook event window (not a tab) */
+ if (event->window == notebook->event_window)
+ {
+ /* create new document */
+ mousepad_window_action_new (NULL, window);
+
+ /* we succeed */
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+
+static gboolean
+mousepad_window_notebook_button_release_event (GtkNotebook *notebook,
+ GdkEventButton *event,
+ MousepadWindow *window)
+{
+ mousepad_return_val_if_fail (MOUSEPAD_IS_WINDOW (window), FALSE);
+ mousepad_return_val_if_fail (MOUSEPAD_IS_DOCUMENT (window->active), FALSE);
+
+ /* focus the active textview */
+ mousepad_document_focus_textview (window->active);
+
+ return FALSE;
+}
+
+
+
+#if GTK_CHECK_VERSION (2,12,0)
+static GtkNotebook *
+mousepad_window_notebook_create_window (GtkNotebook *notebook,
+ GtkWidget *page,
+ gint x,
+ gint y,
+ MousepadWindow *window)
+{
+ MousepadDocument *document;
+
+ mousepad_return_val_if_fail (MOUSEPAD_IS_WINDOW (window), NULL);
+ mousepad_return_val_if_fail (MOUSEPAD_IS_DOCUMENT (page), NULL);
+
+ /* only create new window when there are more then 2 tabs */
+ if (gtk_notebook_get_n_pages (notebook) >= 2)
+ {
+ /* get the document */
+ document = MOUSEPAD_DOCUMENT (page);
+
+ /* take a reference */
+ g_object_ref (G_OBJECT (document));
+
+ /* remove the document from the active window */
+ gtk_container_remove (GTK_CONTAINER (window->notebook), page);
+
+ /* emit the new window with document signal */
+ g_signal_emit (G_OBJECT (window), window_signals[NEW_WINDOW_WITH_DOCUMENT], 0, document, x, y);
+
+ /* release our reference */
+ g_object_unref (G_OBJECT (document));
+ }
+
+ return NULL;
+}
+#endif
+
+
+
+/**
+ * Document Signals Functions
+ **/
+static void
+mousepad_window_modified_changed (MousepadWindow *window)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+
+ mousepad_window_set_title (window);
+}
+
+
+
+static void
+mousepad_window_cursor_changed (MousepadDocument *document,
+ gint line,
+ gint column,
+ gint selection,
+ MousepadWindow *window)
+{
+
+
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (document));
+
+ if (window->statusbar)
+ {
+ /* set the new statusbar cursor position and selection length */
+ mousepad_statusbar_set_cursor_position (MOUSEPAD_STATUSBAR (window->statusbar), line, column, selection);
+ }
+}
+
+
+
+static void
+mousepad_window_selection_changed (MousepadDocument *document,
+ gint selection,
+ MousepadWindow *window)
+{
+ GtkAction *action;
+ guint i;
+ const gchar *action_names1[] = { "tabs-to-spaces", "spaces-to-tabs", "duplicate", "strip-trailing" };
+ const gchar *action_names2[] = { "line-up", "line-down" };
+ const gchar *action_names3[] = { "cut", "copy", "delete", "lowercase", "uppercase", "titlecase", "opposite-case" };
+
+ /* sensitivity of the change selection action */
+ action = gtk_action_group_get_action (window->action_group, "change-selection");
+ gtk_action_set_sensitive (action, selection != 0);
+
+ /* actions that are unsensitive during a column selection */
+ for (i = 0; i < G_N_ELEMENTS (action_names1); i++)
+ {
+ action = gtk_action_group_get_action (window->action_group, action_names1[i]);
+ gtk_action_set_sensitive (action, selection == 0 || selection == 1);
+ }
+
+ /* action that are only sensitive for normal selections */
+ for (i = 0; i < G_N_ELEMENTS (action_names2); i++)
+ {
+ action = gtk_action_group_get_action (window->action_group, action_names2[i]);
+ gtk_action_set_sensitive (action, selection == 1);
+ }
+
+ /* actions that are sensitive for all selections with content */
+ for (i = 0; i < G_N_ELEMENTS (action_names3); i++)
+ {
+ action = gtk_action_group_get_action (window->action_group, action_names3[i]);
+ gtk_action_set_sensitive (action, selection > 0);
+ }
+}
+
+
+
+static void
+mousepad_window_overwrite_changed (MousepadDocument *document,
+ gboolean overwrite,
+ MousepadWindow *window)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (document));
+
+ /* set the new overwrite mode in the statusbar */
+ if (window->statusbar)
+ mousepad_statusbar_set_overwrite (MOUSEPAD_STATUSBAR (window->statusbar), overwrite);
+}
+
+
+
+static void
+mousepad_window_language_changed (MousepadDocument *document,
+ GtkSourceLanguage *language,
+ MousepadWindow *window)
+{
+ gchar *path;
+ GtkWidget *item;
+
+ if (!GTK_IS_SOURCE_LANGUAGE (language))
+ goto set_none;
+
+ path = g_strdup_printf ("/main-menu/document-menu/language-menu/"
+ "placeholder-language-section-items/"
+ "language-section-%s/language-%s",
+ gtk_source_language_get_section (language),
+ gtk_source_language_get_id (language));
+ item = gtk_ui_manager_get_widget (window->ui_manager, path);
+ g_free (path);
+
+ /* activate the appropriate menu item for the new language */
+ if (GTK_IS_CHECK_MENU_ITEM (item))
+ {
+ gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), TRUE);
+ goto set_statusbar;
+ }
+
+set_none:
+ item = gtk_ui_manager_get_widget (window->ui_manager,
+ "/main-menu/document-menu/language-menu/language-none");
+ if (GTK_IS_CHECK_MENU_ITEM (item))
+ gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), TRUE);
+
+set_statusbar:
+ /* set the filetype in the statusbar */
+ if (window->statusbar)
+ mousepad_statusbar_set_language (MOUSEPAD_STATUSBAR (window->statusbar), language);
+}
+
+
+
+static void
+mousepad_window_can_undo (MousepadWindow *window,
+ GParamSpec *unused,
+ GObject *buffer)
+{
+ GtkAction *action;
+ gboolean can_undo;
+
+ can_undo = gtk_source_buffer_can_undo (GTK_SOURCE_BUFFER (buffer));
+
+ action = gtk_action_group_get_action (window->action_group, "undo");
+ gtk_action_set_sensitive (action, can_undo);
+}
+
+
+
+static void
+mousepad_window_can_redo (MousepadWindow *window,
+ GParamSpec *unused,
+ GObject *buffer)
+{
+ GtkAction *action;
+ gboolean can_redo;
+
+ can_redo = gtk_source_buffer_can_redo (GTK_SOURCE_BUFFER (buffer));
+
+ action = gtk_action_group_get_action (window->action_group, "redo");
+ gtk_action_set_sensitive (action, can_redo);
+}
+
+
+
+/**
+ * Menu Functions
+ **/
+static void
+mousepad_window_menu_templates_fill (MousepadWindow *window,
+ GtkWidget *menu,
+ const gchar *path)
+{
+ GDir *dir;
+ GSList *files_list = NULL;
+ GSList *dirs_list = NULL;
+ GSList *li;
+ gchar *absolute_path;
+ gchar *label, *dot;
+ const gchar *name;
+ GtkWidget *item, *image, *submenu;
+
+ /* open the directory */
+ dir = g_dir_open (path, 0, NULL);
+
+ /* read the directory */
+ if (G_LIKELY (dir))
+ {
+ /* walk the directory */
+ for (;;)
+ {
+ /* read the filename of the next file */
+ name = g_dir_read_name (dir);
+
+ /* break when we reached the last file */
+ if (G_UNLIKELY (name == NULL))
+ break;
+
+ /* skip hidden files */
+ if (name[0] == '.')
+ continue;
+
+ /* build absolute path */
+ absolute_path = g_build_path (G_DIR_SEPARATOR_S, path, name, NULL);
+
+ /* check if the file is a regular file or directory */
+ if (g_file_test (absolute_path, G_FILE_TEST_IS_DIR))
+ dirs_list = g_slist_insert_sorted (dirs_list, absolute_path, (GCompareFunc) strcmp);
+ else if (g_file_test (absolute_path, G_FILE_TEST_IS_REGULAR))
+ files_list = g_slist_insert_sorted (files_list, absolute_path, (GCompareFunc) strcmp);
+ else
+ g_free (absolute_path);
+ }
+
+ /* close the directory */
+ g_dir_close (dir);
+ }
+
+ /* append the directories */
+ for (li = dirs_list; li != NULL; li = li->next)
+ {
+ /* create a newsub menu for the directory */
+ submenu = gtk_menu_new ();
+ g_object_ref_sink (G_OBJECT (submenu));
+ gtk_menu_set_screen (GTK_MENU (submenu), gtk_widget_get_screen (menu));
+
+ /* fill the menu */
+ mousepad_window_menu_templates_fill (window, submenu, li->data);
+
+ /* check if the sub menu contains items */
+ if (G_LIKELY (GTK_MENU_SHELL (submenu)->children != NULL))
+ {
+ /* create directory label */
+ label = g_filename_display_basename (li->data);
+
+ /* append the menu */
+ item = gtk_image_menu_item_new_with_label (label);
+ gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+ gtk_widget_show (item);
+
+ /* cleanup */
+ g_free (label);
+
+ /* set menu image */
+ image = gtk_image_new_from_icon_name (GTK_STOCK_DIRECTORY, GTK_ICON_SIZE_MENU);
+ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
+ gtk_widget_show (image);
+ }
+
+ /* cleanup */
+ g_free (li->data);
+ g_object_unref (G_OBJECT (submenu));
+ }
+
+ /* append the files */
+ for (li = files_list; li != NULL; li = li->next)
+ {
+ /* create directory label */
+ label = g_filename_display_basename (li->data);
+
+ /* strip the extension from the label */
+ dot = g_utf8_strrchr (label, -1, '.');
+ if (dot != NULL)
+ *dot = '\0';
+
+ /* create menu item */
+ item = gtk_image_menu_item_new_with_label (label);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+ mousepad_object_set_data_full (G_OBJECT (item), "filename", li->data, g_free);
+ g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (mousepad_window_action_new_from_template), window);
+ gtk_widget_show (item);
+
+ /* set menu image */
+ image = gtk_image_new_from_icon_name (GTK_STOCK_FILE, GTK_ICON_SIZE_MENU);
+ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
+ gtk_widget_show (image);
+
+ /* cleanup */
+ g_free (label);
+ }
+
+ /* cleanup */
+ g_slist_free (dirs_list);
+ g_slist_free (files_list);
+}
+
+
+
+static void
+mousepad_window_menu_templates (GtkWidget *item,
+ MousepadWindow *window)
+{
+ GtkWidget *submenu;
+ const gchar *homedir;
+ gchar *templates_path;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (GTK_IS_MENU_ITEM (item));
+ mousepad_return_if_fail (gtk_menu_item_get_submenu (GTK_MENU_ITEM (item)) == NULL);
+
+ /* schedule the idle build of the recent menu */
+ mousepad_window_recent_menu (window);
+
+ /* get the home directory */
+ homedir = g_getenv ("HOME");
+ if (G_UNLIKELY (homedir == NULL))
+ homedir = g_get_home_dir ();
+
+ /* get the templates path */
+ templates_path = g_build_filename (homedir, "Templates", NULL);
+
+ /* check if the directory exists */
+ if (g_file_test (templates_path, G_FILE_TEST_IS_DIR))
+ {
+ /* create submenu */
+ submenu = gtk_menu_new ();
+ g_object_ref_sink (G_OBJECT (submenu));
+ gtk_menu_set_screen (GTK_MENU (submenu), gtk_widget_get_screen (item));
+
+ /* fill the menu */
+ mousepad_window_menu_templates_fill (window, submenu, templates_path);
+
+ /* set the submenu if it contains items, else hide the item */
+ if (G_LIKELY (GTK_MENU_SHELL (submenu)->children != NULL))
+ gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu);
+ else
+ gtk_widget_hide (item);
+
+ /* release */
+ g_object_unref (G_OBJECT (submenu));
+ }
+ else
+ {
+ /* hide the templates menu item */
+ gtk_widget_hide (item);
+ }
+
+ /* cleanup */
+ g_free (templates_path);
+}
+
+
+
+static void
+mousepad_window_menu_tab_sizes (MousepadWindow *window)
+{
+ GtkRadioAction *action;
+ GSList *group = NULL;
+ gint i, size, merge_id;
+ gchar *name, *tmp;
+ gchar **tab_sizes;
+
+ /* lock menu updates */
+ lock_menu_updates++;
+
+ /* get the default tab sizes and active tab size */
+ g_object_get (G_OBJECT (window->preferences), "misc-default-tab-sizes", &tmp, NULL);
+
+ /* get sizes array and free the temp string */
+ tab_sizes = g_strsplit (tmp, ",", -1);
+ g_free (tmp);
+
+ /* create merge id */
+ merge_id = gtk_ui_manager_new_merge_id (window->ui_manager);
+
+ /* add the default sizes to the menu */
+ for (i = 0; tab_sizes[i] != NULL; i++)
+ {
+ /* convert the string to a number */
+ size = CLAMP (atoi (tab_sizes[i]), 1, 32);
+
+ /* create action name */
+ name = g_strdup_printf ("tab-size_%d", size);
+
+ action = gtk_radio_action_new (name, name + 8, NULL, NULL, size);
+ gtk_radio_action_set_group (action, group);
+ group = gtk_radio_action_get_group (action);
+ g_signal_connect (G_OBJECT (action), "activate", G_CALLBACK (mousepad_window_action_tab_size), window);
+ gtk_action_group_add_action_with_accel (window->action_group, GTK_ACTION (action), "");
+
+ /* release the action */
+ g_object_unref (G_OBJECT (action));
+
+ /* add the action to the go menu */
+ gtk_ui_manager_add_ui (window->ui_manager, merge_id,
+ "/main-menu/document-menu/tab-size-menu/placeholder-tab-items",
+ name, name, GTK_UI_MANAGER_MENUITEM, FALSE);
+
+ /* cleanup */
+ g_free (name);
+ }
+
+ /* cleanup the array */
+ g_strfreev (tab_sizes);
+
+ /* create other action */
+ action = gtk_radio_action_new ("tab-size-other", "", _("Set custom tab size"), NULL, 0);
+ gtk_radio_action_set_group (action, group);
+ g_signal_connect (G_OBJECT (action), "activate", G_CALLBACK (mousepad_window_action_tab_size), window);
+ gtk_action_group_add_action_with_accel (window->action_group, GTK_ACTION (action), "");
+
+ /* release the action */
+ g_object_unref (G_OBJECT (action));
+
+ /* add the action to the go menu */
+ gtk_ui_manager_add_ui (window->ui_manager, merge_id,
+ "/main-menu/document-menu/tab-size-menu/placeholder-tab-items",
+ "tab-size-other", "tab-size-other", GTK_UI_MANAGER_MENUITEM, FALSE);
+
+ /* unlock */
+ lock_menu_updates--;
+}
+
+
+
+static void
+mousepad_window_menu_tab_sizes_update (MousepadWindow *window)
+{
+ gint tab_size;
+ gchar *name, *label = NULL;
+ GtkAction *action;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+
+ /* avoid menu actions */
+ lock_menu_updates++;
+
+ /* get tab size of active document */
+ tab_size = mousepad_view_get_tab_size (window->active->textview);
+
+ /* check if there is a default item with this number */
+ name = g_strdup_printf ("tab-size_%d", tab_size);
+ action = gtk_action_group_get_action (window->action_group, name);
+ g_free (name);
+
+ if (action)
+ {
+ /* toggle the default action */
+ gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
+ }
+ else
+ {
+ /* create suitable label for the other menu */
+ label = g_strdup_printf (_("Ot_her (%d)..."), tab_size);
+ }
+
+ /* get other action */
+ action = gtk_action_group_get_action (window->action_group, "tab-size-other");
+
+ /* toggle other action if needed */
+ if (label)
+ gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
+
+ /* set action label */
+ g_object_set (G_OBJECT (action), "label", label ? label : _("Ot_her..."), NULL);
+
+ /* cleanup */
+ g_free (label);
+
+ /* allow menu actions again */
+ lock_menu_updates--;
+}
+
+
+
+static void
+mousepad_window_menu_textview_deactivate (GtkWidget *menu,
+ GtkTextView *textview)
+{
+ mousepad_return_if_fail (GTK_IS_TEXT_VIEW (textview));
+ mousepad_return_if_fail (textview->popup_menu == menu);
+
+ /* disconnect this signal */
+ g_signal_handlers_disconnect_by_func (G_OBJECT (menu), mousepad_window_menu_textview_deactivate, textview);
+
+ /* unset the popup menu since your menu is owned by the ui manager */
+ GTK_TEXT_VIEW (textview)->popup_menu = NULL;
+}
+
+
+
+static void
+mousepad_window_menu_textview_popup (GtkTextView *textview,
+ GtkMenu *old_menu,
+ MousepadWindow *window)
+{
+ GtkWidget *menu;
+
+ mousepad_return_if_fail (GTK_WIDGET (old_menu) == textview->popup_menu);
+ mousepad_return_if_fail (GTK_IS_TEXT_VIEW (textview));
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+ mousepad_return_if_fail (GTK_IS_MENU (textview->popup_menu));
+
+ /* destroy origional menu */
+ gtk_widget_destroy (textview->popup_menu);
+
+ /* get the textview menu */
+ menu = gtk_ui_manager_get_widget (window->ui_manager, "/textview-menu");
+
+ /* connect signal */
+ g_signal_connect (G_OBJECT (menu), "deactivate", G_CALLBACK (mousepad_window_menu_textview_deactivate), textview);
+
+ /* set screen */
+ gtk_menu_set_screen (GTK_MENU (menu), gtk_widget_get_screen (GTK_WIDGET (textview)));
+
+ /* set ours */
+ textview->popup_menu = menu;
+}
+
+
+
+static void
+mousepad_window_update_actions (MousepadWindow *window)
+{
+ GtkAction *action;
+ GtkNotebook *notebook = GTK_NOTEBOOK (window->notebook);
+ MousepadDocument *document = window->active;
+ gboolean cycle_tabs;
+ gint n_pages;
+ gint page_num;
+ gboolean active, sensitive;
+ MousepadLineEnding line_ending;
+ const gchar *action_name;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+
+ /* update the actions for the active document */
+ if (G_LIKELY (document))
+ {
+ /* avoid menu actions */
+ lock_menu_updates++;
+
+ /* determine the number of pages and the current page number */
+ n_pages = gtk_notebook_get_n_pages (notebook);
+ page_num = gtk_notebook_page_num (notebook, GTK_WIDGET (document));
+
+ /* whether we cycle tabs */
+ g_object_get (G_OBJECT (window->preferences), "misc-cycle-tabs", &cycle_tabs, NULL);
+
+ /* set the sensitivity of the back and forward buttons in the go menu */
+ action = gtk_action_group_get_action (window->action_group, "back");
+ gtk_action_set_sensitive (action, (cycle_tabs && n_pages > 1) || (page_num > 0));
+
+ action = gtk_action_group_get_action (window->action_group, "forward");
+ gtk_action_set_sensitive (action, (cycle_tabs && n_pages > 1 ) || (page_num < n_pages - 1));
+
+ /* set the reload, detach and save sensitivity */
+ action = gtk_action_group_get_action (window->action_group, "save");
+ gtk_action_set_sensitive (action, !mousepad_file_get_read_only (document->file));
+
+ action = gtk_action_group_get_action (window->action_group, "detach");
+ gtk_action_set_sensitive (action, (n_pages > 1));
+
+ action = gtk_action_group_get_action (window->action_group, "revert");
+ gtk_action_set_sensitive (action, mousepad_file_get_filename (document->file) != NULL);
+
+ /* line ending type */
+ line_ending = mousepad_file_get_line_ending (document->file);
+ if (G_UNLIKELY (line_ending == MOUSEPAD_EOL_MAC))
+ action_name = "mac";
+ else if (G_UNLIKELY (line_ending == MOUSEPAD_EOL_DOS))
+ action_name = "dos";
+ else
+ action_name = "unix";
+
+ /* set the corrent line ending type */
+ action = gtk_action_group_get_action (window->action_group, action_name);
+ gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
+
+ /* write bom */
+ action = gtk_action_group_get_action (window->action_group, "write-bom");
+ gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), mousepad_file_get_write_bom (document->file, &sensitive));
+ gtk_action_set_sensitive (action, sensitive);
+
+ /* toggle the document settings */
+ active = mousepad_document_get_word_wrap (document);
+ action = gtk_action_group_get_action (window->action_group, "word-wrap");
+ gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), active);
+
+ active = mousepad_view_get_line_numbers (document->textview);
+ action = gtk_action_group_get_action (window->action_group, "line-numbers");
+ gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), active);
+
+ active = mousepad_view_get_auto_indent (document->textview);
+ action = gtk_action_group_get_action (window->action_group, "auto-indent");
+ gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), active);
+
+ /* update the tabs size menu */
+ mousepad_window_menu_tab_sizes_update (window);
+
+ active = mousepad_view_get_insert_spaces (document->textview);
+ action = gtk_action_group_get_action (window->action_group, "insert-spaces");
+ gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), active);
+
+ /* set the sensitivity of the undo and redo actions */
+ mousepad_window_can_undo (window, NULL, G_OBJECT (document->buffer));
+ mousepad_window_can_redo (window, NULL, G_OBJECT (document->buffer));
+
+ /* active this tab in the go menu */
+ action = mousepad_object_get_data (G_OBJECT (document), "navigation-menu-action");
+ if (G_LIKELY (action != NULL))
+ gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
+
+ /* allow menu actions again */
+ lock_menu_updates--;
+ }
+}
+
+
+
+static gboolean
+mousepad_window_update_gomenu_idle (gpointer user_data)
+{
+ MousepadDocument *document;
+ MousepadWindow *window;
+ gint npages;
+ gint n;
+ gchar name[32];
+ const gchar *title;
+ const gchar *tooltip;
+ gchar accelerator[7];
+ GtkRadioAction *radio_action;
+ GSList *group = NULL;
+ GList *actions, *iter;
+
+ mousepad_return_val_if_fail (MOUSEPAD_IS_WINDOW (user_data), FALSE);
+
+ GDK_THREADS_ENTER ();
+
+ /* get the window */
+ window = MOUSEPAD_WINDOW (user_data);
+
+ /* prevent menu updates */
+ lock_menu_updates++;
+
+ /* remove the old merge */
+ if (window->gomenu_merge_id != 0)
+ {
+ gtk_ui_manager_remove_ui (window->ui_manager, window->gomenu_merge_id);
+
+ /* drop all the old recent items from the menu */
+ actions = gtk_action_group_list_actions (window->action_group);
+ for (iter = actions; iter != NULL; iter = g_list_next (iter))
+ {
+ /* match only actions starting with "mousepad-tab-" */
+ if (g_str_has_prefix (gtk_action_get_name (iter->data), "mousepad-tab-"))
+ gtk_action_group_remove_action (window->action_group, iter->data);
+ }
+ g_list_free (actions);
+ }
+
+ /* create a new merge id */
+ window->gomenu_merge_id = gtk_ui_manager_new_merge_id (window->ui_manager);
+
+ /* walk through the notebook pages */
+ npages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (window->notebook));
+
+ for (n = 0; n < npages; ++n)
+ {
+ document = MOUSEPAD_DOCUMENT (gtk_notebook_get_nth_page (GTK_NOTEBOOK (window->notebook), n));
+
+ /* create a new action name */
+ g_snprintf (name, sizeof (name), "mousepad-tab-%d", n);
+
+ /* get the name and file name */
+ title = mousepad_document_get_basename (document);
+ tooltip = mousepad_document_get_filename (document);
+
+ /* create the radio action */
+ radio_action = gtk_radio_action_new (name, title, tooltip, NULL, n);
+ gtk_radio_action_set_group (radio_action, group);
+ group = gtk_radio_action_get_group (radio_action);
+ g_signal_connect (G_OBJECT (radio_action), "activate", G_CALLBACK (mousepad_window_action_go_to_tab), window->notebook);
+
+ /* connect the action to the document to we can easily active it when the user switched from tab */
+ mousepad_object_set_data (G_OBJECT (document), "navigation-menu-action", radio_action);
+
+ if (G_LIKELY (n < 9))
+ {
+ /* create an accelerator and add it to the menu */
+ g_snprintf (accelerator, sizeof (accelerator), "<Alt>%d", n + 1);
+ gtk_action_group_add_action_with_accel (window->action_group, GTK_ACTION (radio_action), accelerator);
+ }
+ else
+ /* add a menu item without accelerator */
+ gtk_action_group_add_action (window->action_group, GTK_ACTION (radio_action));
+
+ /* select the active entry */
+ if (gtk_notebook_get_current_page (GTK_NOTEBOOK (window->notebook)) == n)
+ gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (radio_action), TRUE);
+
+ /* release the action */
+ g_object_unref (G_OBJECT (radio_action));
+
+ /* add the action to the go menu */
+ gtk_ui_manager_add_ui (window->ui_manager, window->gomenu_merge_id,
+ "/main-menu/navigation-menu/placeholder-file-items",
+ name, name, GTK_UI_MANAGER_MENUITEM, FALSE);
+ }
+
+ /* release our lock */
+ lock_menu_updates--;
+
+ GDK_THREADS_LEAVE ();
+
+ return FALSE;
+}
+
+
+
+static void
+mousepad_window_update_gomenu_idle_destroy (gpointer user_data)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (user_data));
+
+ MOUSEPAD_WINDOW (user_data)->update_go_menu_id = 0;
+}
+
+
+
+static void
+mousepad_window_update_gomenu (MousepadWindow *window)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+
+ /* leave when we're updating multiple files or there is this an idle function pending */
+ if (lock_menu_updates && window->update_go_menu_id != 0)
+ return;
+
+ /* schedule a go menu update */
+ window->update_go_menu_id = g_idle_add_full (G_PRIORITY_LOW, mousepad_window_update_gomenu_idle,
+ window, mousepad_window_update_gomenu_idle_destroy);
+}
+
+
+
+/**
+ * Funtions for managing the recent files
+ **/
+static void
+mousepad_window_recent_add (MousepadWindow *window,
+ MousepadFile *file)
+{
+ GtkRecentData info;
+ gchar *uri;
+ gchar *description;
+ const gchar *charset;
+ static gchar *groups[] = { (gchar *) PACKAGE_NAME, NULL };
+
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_FILE (file));
+
+ /* get the charset */
+ charset = mousepad_encoding_get_charset (mousepad_file_get_encoding (file));
+
+ /* build description */
+ description = g_strdup_printf ("%s: %s", _("Charset"), charset);
+
+ /* create the recent data */
+ info.display_name = NULL;
+ info.description = (gchar *) description;
+ info.mime_type = (gchar *) "text/plain";
+ info.app_name = (gchar *) PACKAGE_NAME;
+ info.app_exec = (gchar *) PACKAGE " %u";
+ info.groups = groups;
+ info.is_private = FALSE;
+
+ /* create an uri from the filename */
+ uri = mousepad_file_get_uri (file);
+
+ if (G_LIKELY (uri != NULL))
+ {
+ /* make sure the recent manager is initialized */
+ mousepad_window_recent_manager_init (window);
+
+ /* add the new recent info to the recent manager */
+ gtk_recent_manager_add_full (window->recent_manager, uri, &info);
+
+ /* cleanup */
+ g_free (uri);
+ }
+
+ /* cleanup */
+ g_free (description);
+}
+
+
+
+static gint
+mousepad_window_recent_sort (GtkRecentInfo *a,
+ GtkRecentInfo *b)
+{
+ return (gtk_recent_info_get_modified (a) < gtk_recent_info_get_modified (b));
+}
+
+
+
+static void
+mousepad_window_recent_manager_init (MousepadWindow *window)
+{
+ /* set recent manager if not already done */
+ if (G_UNLIKELY (window->recent_manager == NULL))
+ {
+ /* get the default manager */
+ window->recent_manager = gtk_recent_manager_get_default ();
+
+ /* connect changed signal */
+ g_signal_connect_swapped (G_OBJECT (window->recent_manager), "changed", G_CALLBACK (mousepad_window_recent_menu), window);
+ }
+}
+
+
+
+static gboolean
+mousepad_window_recent_menu_idle (gpointer user_data)
+{
+ MousepadWindow *window = MOUSEPAD_WINDOW (user_data);
+ GList *items, *li;
+ GList *filtered = NULL;
+ GtkRecentInfo *info;
+ const gchar *uri;
+ const gchar *display_name;
+ gchar *tooltip, *label;
+ gchar *filename, *filename_utf8;
+ GtkAction *action;
+ gchar name[32];
+ gint n, i;
+
+ GDK_THREADS_ENTER ();
+
+ if (window->recent_merge_id != 0)
+ {
+ /* unmerge the ui controls from the previous update */
+ gtk_ui_manager_remove_ui (window->ui_manager, window->recent_merge_id);
+
+ /* drop all the old recent items from the menu */
+ for (i = 1; i < 100 /* arbitrary */; i++)
+ {
+ g_snprintf (name, sizeof (name), "recent-info-%d", i);
+ action = gtk_action_group_get_action (window->action_group, name);
+ if (G_UNLIKELY (action == NULL))
+ break;
+ gtk_action_group_remove_action (window->action_group, action);
+ }
+ }
+
+ /* create a new merge id */
+ window->recent_merge_id = gtk_ui_manager_new_merge_id (window->ui_manager);
+
+ /* make sure the recent manager is initialized */
+ mousepad_window_recent_manager_init (window);
+
+ /* get all the items in the manager */
+ items = gtk_recent_manager_get_items (window->recent_manager);
+
+ /* walk through the items in the manager and pick the ones that or in the mousepad group */
+ for (li = items; li != NULL; li = li->next)
+ {
+ /* check if the item is in the Mousepad group */
+ if (!gtk_recent_info_has_group (li->data, PACKAGE_NAME))
+ continue;
+
+ /* insert the the list, sorted by date */
+ filtered = g_list_insert_sorted (filtered, li->data, (GCompareFunc) mousepad_window_recent_sort);
+ }
+
+ /* get the recent menu limit number */
+ g_object_get (G_OBJECT (window->preferences), "misc-recent-menu-items", &n, NULL);
+
+ /* append the items to the menu */
+ for (li = filtered, i = 1; n > 0 && li != NULL; li = li->next)
+ {
+ info = li->data;
+
+ /* get the filename */
+ uri = gtk_recent_info_get_uri (info);
+ filename = g_filename_from_uri (uri, NULL, NULL);
+
+ /* append to the menu if the file exists, else remove it from the history */
+ if (filename && g_file_test (filename, G_FILE_TEST_EXISTS))
+ {
+ /* create the action name */
+ g_snprintf (name, sizeof (name), "recent-info-%d", i);
+
+ /* get the name of the item and escape the underscores */
+ display_name = gtk_recent_info_get_display_name (info);
+ label = mousepad_util_escape_underscores (display_name);
+
+ /* create and utf-8 valid version of the filename */
+ filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
+ tooltip = g_strdup_printf (_("Open '%s'"), filename_utf8);
+ g_free (filename_utf8);
+
+ /* create the action */
+ action = gtk_action_new (name, label, tooltip, NULL);
+
+ /* cleanup */
+ g_free (tooltip);
+ g_free (label);
+
+ /* add the info data and connect a menu signal */
+ mousepad_object_set_data_full (G_OBJECT (action), "gtk-recent-info", gtk_recent_info_ref (info), gtk_recent_info_unref);
+ g_signal_connect (G_OBJECT (action), "activate", G_CALLBACK (mousepad_window_action_open_recent), window);
+
+ /* add the action to the recent actions group */
+ gtk_action_group_add_action (window->action_group, action);
+
+ /* release the action */
+ g_object_unref (G_OBJECT (action));
+
+ /* add the action to the menu */
+ gtk_ui_manager_add_ui (window->ui_manager, window->recent_merge_id,
+ "/main-menu/file-menu/recent-menu/placeholder-recent-items",
+ name, name, GTK_UI_MANAGER_MENUITEM, FALSE);
+
+ /* update couters */
+ n--;
+ i++;
+ }
+ else
+ {
+ /* remove the item. don't both the user if this fails */
+ gtk_recent_manager_remove_item (window->recent_manager, uri, NULL);
+ }
+
+ /* cleanup */
+ g_free (filename);
+ }
+
+ /* set the visibility of the 'no items found' action */
+ action = gtk_action_group_get_action (window->action_group, "no-recent-items");
+ gtk_action_set_visible (action, (filtered == NULL));
+ gtk_action_set_sensitive (action, FALSE);
+
+ /* set the sensitivity of the clear button */
+ action = gtk_action_group_get_action (window->action_group, "clear-recent");
+ gtk_action_set_sensitive (action, (filtered != NULL));
+
+ /* cleanup */
+ g_list_foreach (items, (GFunc) gtk_recent_info_unref, NULL);
+ g_list_free (items);
+ g_list_free (filtered);
+
+ GDK_THREADS_LEAVE ();
+
+ /* stop the idle function */
+ return FALSE;
+}
+
+
+
+static void
+mousepad_window_recent_menu_idle_destroy (gpointer user_data)
+{
+ MOUSEPAD_WINDOW (user_data)->update_recent_menu_id = 0;
+}
+
+
+
+static void
+mousepad_window_recent_menu (MousepadWindow *window)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+
+ /* leave when we're updating multiple files or there is this an idle function pending */
+ if (lock_menu_updates > 0 || window->update_recent_menu_id != 0)
+ return;
+
+ /* schedule a recent menu update */
+ window->update_recent_menu_id = g_idle_add_full (G_PRIORITY_LOW, mousepad_window_recent_menu_idle,
+ window, mousepad_window_recent_menu_idle_destroy);
+}
+
+
+
+static const gchar *
+mousepad_window_recent_get_charset (GtkRecentInfo *info)
+{
+ const gchar *description;
+ const gchar *charset = NULL;
+ guint offset;
+
+ /* get the description */
+ description = gtk_recent_info_get_description (info);
+ if (G_LIKELY (description))
+ {
+ /* get the offset length: 'Encoding: ' */
+ offset = strlen (_("Charset")) + 2;
+
+ /* check if the encoding string looks valid, if so, set it */
+ if (G_LIKELY (strlen (description) > offset))
+ charset = description + offset;
+ }
+
+ return charset;
+}
+
+
+
+static void
+mousepad_window_recent_clear (MousepadWindow *window)
+{
+ GList *items, *li;
+ const gchar *uri;
+ GError *error = NULL;
+ GtkRecentInfo *info;
+
+ /* make sure the recent manager is initialized */
+ mousepad_window_recent_manager_init (window);
+
+ /* get all the items in the manager */
+ items = gtk_recent_manager_get_items (window->recent_manager);
+
+ /* walk through the items */
+ for (li = items; li != NULL; li = li->next)
+ {
+ info = li->data;
+
+ /* check if the item is in the Mousepad group */
+ if (!gtk_recent_info_has_group (info, PACKAGE_NAME))
+ continue;
+
+ /* get the uri of the recent item */
+ uri = gtk_recent_info_get_uri (info);
+
+ /* try to remove it, if it fails, break the loop to avoid multiple errors */
+ if (G_UNLIKELY (gtk_recent_manager_remove_item (window->recent_manager, uri, &error) == FALSE))
+ break;
+ }
+
+ /* cleanup */
+ g_list_foreach (items, (GFunc) gtk_recent_info_unref, NULL);
+ g_list_free (items);
+
+ /* print a warning is there is one */
+ if (G_UNLIKELY (error != NULL))
+ {
+ mousepad_dialogs_show_error (GTK_WINDOW (window), error, _("Failed to clear the recent history"));
+ g_error_free (error);
+ }
+}
+
+
+
+/**
+ * Drag and drop functions
+ **/
+static void
+mousepad_window_drag_data_received (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ GtkSelectionData *selection_data,
+ guint info,
+ guint drag_time,
+ MousepadWindow *window)
+{
+ gchar **uris;
+ gchar *working_directory;
+ GtkWidget *notebook, **document;
+ GtkWidget *child, *label;
+ gint i, n_pages;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
+
+ /* we only accept text/uri-list drops with format 8 and atleast one byte of data */
+ if (info == TARGET_TEXT_URI_LIST && selection_data->format == 8 && selection_data->length > 0)
+ {
+ /* extract the uris from the data */
+ uris = g_uri_list_extract_uris ((const gchar *)selection_data->data);
+
+ /* get working directory */
+ working_directory = g_get_current_dir ();
+
+ /* open the files */
+ mousepad_window_open_files (window, working_directory, uris);
+
+ /* cleanup */
+ g_free (working_directory);
+ g_strfreev (uris);
+
+ /* finish the drag (copy) */
+ gtk_drag_finish (context, TRUE, FALSE, drag_time);
+ }
+ else if (info == TARGET_GTK_NOTEBOOK_TAB)
+ {
+ /* get the source notebook */
+ notebook = gtk_drag_get_source_widget (context);
+
+ /* get the document that has been dragged */
+ document = (GtkWidget **) selection_data->data;
+
+ /* check */
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (*document));
+
+ /* take a reference on the document before we remove it */
+ g_object_ref (G_OBJECT (*document));
+
+ /* remove the document from the source window */
+ gtk_container_remove (GTK_CONTAINER (notebook), *document);
+
+ /* get the number of pages in the notebook */
+ n_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (window->notebook));
+
+ /* figure out where to insert the tab in the notebook */
+ for (i = 0; i < n_pages; i++)
+ {
+ /* get the child label */
+ child = gtk_notebook_get_nth_page (GTK_NOTEBOOK (window->notebook), i);
+ label = gtk_notebook_get_tab_label (GTK_NOTEBOOK (window->notebook), child);
+
+ /* break if we have a matching drop position */
+ if (x < (label->allocation.x + label->allocation.width / 2))
+ break;
+ }
+
+ /* add the document to the new window */
+ mousepad_window_add (window, MOUSEPAD_DOCUMENT (*document));
+
+ /* move the tab to the correct position */
+ gtk_notebook_reorder_child (GTK_NOTEBOOK (window->notebook), *document, i);
+
+ /* release our reference on the document */
+ g_object_unref (G_OBJECT (*document));
+
+ /* finish the drag (move) */
+ gtk_drag_finish (context, TRUE, TRUE, drag_time);
+ }
+}
+
+
+
+/**
+ * Find and replace
+ **/
+static gint
+mousepad_window_search (MousepadWindow *window,
+ MousepadSearchFlags flags,
+ const gchar *string,
+ const gchar *replacement)
+{
+ gint nmatches = 0;
+ gint npages, i;
+ GtkWidget *document;
+
+ mousepad_return_val_if_fail (MOUSEPAD_IS_WINDOW (window), -1);
+
+ if (flags & MOUSEPAD_SEARCH_FLAGS_ACTION_HIGHTLIGHT)
+ {
+ /* highlight all the matches */
+ nmatches = mousepad_util_highlight (window->active->buffer, window->active->tag, string, flags);
+ }
+ else if (flags & MOUSEPAD_SEARCH_FLAGS_ALL_DOCUMENTS)
+ {
+ /* get the number of documents in this window */
+ npages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (window->notebook));
+
+ /* walk the pages */
+ for (i = 0; i < npages; i++)
+ {
+ /* get the document */
+ document = gtk_notebook_get_nth_page (GTK_NOTEBOOK (window->notebook), i);
+
+ /* replace the matches in the document */
+ nmatches += mousepad_util_search (MOUSEPAD_DOCUMENT (document)->buffer, string, replacement, flags);
+ }
+ }
+ else if (window->active != NULL)
+ {
+ /* search or replace in the active document */
+ nmatches = mousepad_util_search (window->active->buffer, string, replacement, flags);
+
+ /* make sure the selection is visible */
+ if (flags & (MOUSEPAD_SEARCH_FLAGS_ACTION_SELECT | MOUSEPAD_SEARCH_FLAGS_ACTION_REPLACE) && nmatches > 0)
+ mousepad_view_scroll_to_cursor (window->active->textview);
+ }
+ else
+ {
+ /* should never be reaches */
+ mousepad_assert_not_reached ();
+ }
+
+ return nmatches;
+}
+
+
+
+/**
+ * Search Bar
+ **/
+static void
+mousepad_window_hide_search_bar (MousepadWindow *window)
+{
+ MousepadSearchFlags flags;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+ mousepad_return_if_fail (MOUSEPAD_IS_SEARCH_BAR (window->search_bar));
+
+ /* setup flags */
+ flags = MOUSEPAD_SEARCH_FLAGS_ACTION_HIGHTLIGHT
+ | MOUSEPAD_SEARCH_FLAGS_ACTION_CLEANUP;
+
+ /* remove the highlight */
+ mousepad_window_search (window, flags, NULL, NULL);
+
+ /* hide the search bar */
+ gtk_widget_hide (window->search_bar);
+
+ /* focus the active document's text view */
+ mousepad_document_focus_textview (window->active);
+}
+
+
+
+/**
+ * Paste from History
+ **/
+static void
+mousepad_window_paste_history_add (MousepadWindow *window)
+{
+ GtkClipboard *clipboard;
+ gchar *text;
+ GSList *li;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+
+ /* get the current clipboard text */
+ clipboard = gtk_widget_get_clipboard (GTK_WIDGET (window), GDK_SELECTION_CLIPBOARD);
+ text = gtk_clipboard_wait_for_text (clipboard);
+
+ /* leave when there is no text */
+ if (G_UNLIKELY (text == NULL))
+ return;
+
+ /* check if the item is already in the history */
+ for (li = clipboard_history; li != NULL; li = li->next)
+ if (strcmp (li->data, text) == 0)
+ break;
+
+ /* append the item or remove it */
+ if (G_LIKELY (li == NULL))
+ {
+ /* add to the list */
+ clipboard_history = g_slist_prepend (clipboard_history, text);
+
+ /* get the 10th item from the list and remove it if it exists */
+ li = g_slist_nth (clipboard_history, 10);
+ if (li != NULL)
+ {
+ /* cleanup */
+ g_free (li->data);
+ clipboard_history = g_slist_delete_link (clipboard_history, li);
+ }
+ }
+ else
+ {
+ /* already in the history, remove it */
+ g_free (text);
+ }
+}
+
+
+
+static void
+mousepad_window_paste_history_menu_position (GtkMenu *menu,
+ gint *x,
+ gint *y,
+ gboolean *push_in,
+ gpointer user_data)
+{
+ MousepadWindow *window = MOUSEPAD_WINDOW (user_data);
+ MousepadDocument *document = window->active;
+ GtkTextIter iter;
+ GtkTextMark *mark;
+ GdkRectangle location;
+ gint iter_x, iter_y;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (document));
+ mousepad_return_if_fail (GTK_IS_TEXT_VIEW (document->textview));
+ mousepad_return_if_fail (GTK_IS_TEXT_BUFFER (document->buffer));
+
+ /* get the root coordinates of the texview widget */
+ gdk_window_get_origin (gtk_text_view_get_window (GTK_TEXT_VIEW (document->textview), GTK_TEXT_WINDOW_TEXT), x, y);
+
+ /* get the cursor iter */
+ mark = gtk_text_buffer_get_insert (document->buffer);
+ gtk_text_buffer_get_iter_at_mark (document->buffer, &iter, mark);
+
+ /* get iter location */
+ gtk_text_view_get_iter_location (GTK_TEXT_VIEW (document->textview), &iter, &location);
+
+ /* convert to textview coordinates */
+ gtk_text_view_buffer_to_window_coords (GTK_TEXT_VIEW (document->textview), GTK_TEXT_WINDOW_TEXT,
+ location.x, location.y, &iter_x, &iter_y);
+
+ /* add the iter coordinates to the menu popup position */
+ *x += iter_x;
+ *y += iter_y + location.height;
+}
+
+
+
+static void
+mousepad_window_paste_history_activate (GtkMenuItem *item,
+ MousepadWindow *window)
+{
+ const gchar *text;
+
+ mousepad_return_if_fail (GTK_IS_MENU_ITEM (item));
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+ mousepad_return_if_fail (MOUSEPAD_IS_VIEW (window->active->textview));
+
+ /* get the menu item text */
+ text = mousepad_object_get_data (G_OBJECT (item), "history-pointer");
+
+ /* paste the text */
+ if (G_LIKELY (text))
+ mousepad_view_clipboard_paste (window->active->textview, text, FALSE);
+}
+
+
+
+static GtkWidget *
+mousepad_window_paste_history_menu_item (const gchar *text,
+ const gchar *mnemonic)
+{
+ GtkWidget *item;
+ GtkWidget *label;
+ GtkWidget *hbox;
+ const gchar *s;
+ gchar *label_str;
+ GString *string;
+
+ /* create new label string */
+ string = g_string_sized_new (PASTE_HISTORY_MENU_LENGTH);
+
+ /* get the first 30 chars of the clipboard text */
+ if (g_utf8_strlen (text, -1) > PASTE_HISTORY_MENU_LENGTH)
+ {
+ /* append the first 30 chars */
+ s = g_utf8_offset_to_pointer (text, PASTE_HISTORY_MENU_LENGTH);
+ string = g_string_append_len (string, text, s - text);
+
+ /* make it look like a ellipsized string */
+ string = g_string_append (string, "...");
+ }
+ else
+ {
+ /* append the entire string */
+ string = g_string_append (string, text);
+ }
+
+ /* get the string */
+ label_str = g_string_free (string, FALSE);
+
+ /* replace tab and new lines with spaces */
+ label_str = g_strdelimit (label_str, "\n\t\r", ' ');
+
+ /* create a new item */
+ item = gtk_menu_item_new ();
+
+ /* create a hbox */
+ hbox = gtk_hbox_new (FALSE, 14);
+ gtk_container_add (GTK_CONTAINER (item), hbox);
+ gtk_widget_show (hbox);
+
+ /* create the clipboard label */
+ label = gtk_label_new (label_str);
+ gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+ gtk_widget_show (label);
+
+ /* create the mnemonic label */
+ label = gtk_label_new_with_mnemonic (mnemonic);
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+ gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), item);
+ gtk_widget_show (label);
+
+ /* cleanup */
+ g_free (label_str);
+
+ return item;
+}
+
+
+
+static GtkWidget *
+mousepad_window_paste_history_menu (MousepadWindow *window)
+{
+ GSList *li;
+ gchar *text;
+ gpointer list_data = NULL;
+ GtkWidget *item;
+ GtkWidget *menu;
+ GtkClipboard *clipboard;
+ gchar mnemonic[4];
+ gint n;
+
+ mousepad_return_val_if_fail (MOUSEPAD_IS_WINDOW (window), NULL);
+
+ /* create new menu and set the screen */
+ menu = gtk_menu_new ();
+ g_object_ref_sink (G_OBJECT (menu));
+ g_signal_connect (G_OBJECT (menu), "deactivate", G_CALLBACK (g_object_unref), NULL);
+ gtk_menu_set_screen (GTK_MENU (menu), gtk_widget_get_screen (GTK_WIDGET (window)));
+
+ /* get the current clipboard text */
+ clipboard = gtk_widget_get_clipboard (GTK_WIDGET (window), GDK_SELECTION_CLIPBOARD);
+ text = gtk_clipboard_wait_for_text (clipboard);
+
+ /* append the history items */
+ for (li = clipboard_history, n = 1; li != NULL; li = li->next)
+ {
+ /* skip the active clipboard item */
+ if (G_UNLIKELY (list_data == NULL && text && strcmp (li->data, text) == 0))
+ {
+ /* store the pointer so we can attach it at the end of the menu */
+ list_data = li->data;
+ }
+ else
+ {
+ /* create mnemonic string */
+ g_snprintf (mnemonic, sizeof (mnemonic), "_%d", n++);
+
+ /* create menu item */
+ item = mousepad_window_paste_history_menu_item (li->data, mnemonic);
+ mousepad_object_set_data (G_OBJECT (item), "history-pointer", li->data);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+ g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (mousepad_window_paste_history_activate), window);
+ gtk_widget_show (item);
+ }
+ }
+
+ /* cleanup */
+ g_free (text);
+
+ if (list_data != NULL)
+ {
+ /* add separator between history and active menu items */
+ if (GTK_MENU_SHELL (menu)->children != NULL)
+ {
+ item = gtk_separator_menu_item_new ();
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+ gtk_widget_show (item);
+ }
+
+ /* create menu item for current clipboard text */
+ item = mousepad_window_paste_history_menu_item (list_data, "_0");
+ mousepad_object_set_data (G_OBJECT (item), "history-pointer", list_data);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+ g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (mousepad_window_paste_history_activate), window);
+ gtk_widget_show (item);
+ }
+ else if (GTK_MENU_SHELL (menu)->children == NULL)
+ {
+ /* create an item to inform the user */
+ item = gtk_menu_item_new_with_label (_("No clipboard data"));
+ gtk_widget_set_sensitive (item, FALSE);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+ gtk_widget_show (item);
+ }
+
+ return menu;
+}
+
+
+
+/**
+ * Miscellaneous Actions
+ **/
+static void
+mousepad_window_button_close_tab (MousepadDocument *document,
+ MousepadWindow *window)
+{
+ gint page_num;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (document));
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+
+ /* switch to the tab we're going to close */
+ page_num = gtk_notebook_page_num (GTK_NOTEBOOK (window->notebook), GTK_WIDGET (document));
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (window->notebook), page_num);
+
+ /* close the document */
+ mousepad_window_close_document (window, document);
+}
+
+
+
+static gboolean
+mousepad_window_delete_event (MousepadWindow *window,
+ GdkEvent *event)
+{
+ mousepad_return_val_if_fail (MOUSEPAD_IS_WINDOW (window), FALSE);
+
+ /* try to close the window */
+ mousepad_window_action_close_window (NULL, window);
+
+ /* we will close the window when all the tabs are closed */
+ return TRUE;
+}
+
+
+
+static void
+mousepad_window_menu_color_schemes (MousepadWindow *window)
+{
+ GtkRadioAction *action;
+ GSList *group = NULL, *schemes, *iter;
+ gint merge_id;
+ gchar *name, *selected_color_scheme = NULL;
+
+ /* lock menu updates */
+ lock_menu_updates++;
+
+ /* get the previously saved colour scheme name */
+ g_object_get (window->preferences, "view-color-scheme", &selected_color_scheme, NULL);
+
+ /* get list of schemes */
+ schemes = mousepad_util_color_schemes_get_sorted ();
+
+ /* create merge id */
+ merge_id = gtk_ui_manager_new_merge_id (window->ui_manager);
+
+ /* create a "none" action */
+ action = gtk_radio_action_new ("color-scheme-none",
+ _("None"),
+ _("Turn off color schemes"),
+ NULL,
+ g_str_hash ("none"));
+ gtk_radio_action_set_group (action, group);
+ group = gtk_radio_action_get_group (action);
+ g_signal_connect (G_OBJECT (action), "activate", G_CALLBACK (mousepad_window_action_color_scheme), window);
+ gtk_action_group_add_action_with_accel (window->action_group, GTK_ACTION (action), "");
+
+ /* release the action */
+ g_object_unref (G_OBJECT (action));
+
+ /* add the action to the go menu */
+ gtk_ui_manager_add_ui (window->ui_manager, merge_id,
+ "/main-menu/view-menu/color-scheme-menu/placeholder-color-scheme-items",
+ "color-scheme-none", "color-scheme-none", GTK_UI_MANAGER_MENUITEM, FALSE);
+
+ /* add a separator */
+ gtk_ui_manager_add_ui (window->ui_manager, merge_id,
+ "/main-menu/view-menu/color-scheme-menu/placeholder-color-scheme-items",
+ "color-scheme-separator", NULL, GTK_UI_MANAGER_SEPARATOR, FALSE);
+
+ /* add the color schemes to the menu */
+ for (iter = schemes; iter != NULL; iter = g_slist_next (iter))
+ {
+ /* create action name */
+ name = g_strdup_printf ("color-scheme_%s", gtk_source_style_scheme_get_id (iter->data));
+
+ /* create action for colour scheme */
+ action = gtk_radio_action_new (name,
+ gtk_source_style_scheme_get_name (iter->data),
+ gtk_source_style_scheme_get_description (iter->data),
+ NULL,
+ g_str_hash (gtk_source_style_scheme_get_id (iter->data)));
+ gtk_radio_action_set_group (action, group);
+ group = gtk_radio_action_get_group (action);
+ g_signal_connect (G_OBJECT (action), "activate", G_CALLBACK (mousepad_window_action_color_scheme), window);
+ gtk_action_group_add_action_with_accel (window->action_group, GTK_ACTION (action), "");
+
+ /* activate the radio button if it was the last saved colour scheme */
+ if (g_strcmp0 (gtk_source_style_scheme_get_id (iter->data), selected_color_scheme) == 0)
+ gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
+
+ /* release the action */
+ g_object_unref (G_OBJECT (action));
+
+ /* add the action to the go menu */
+ gtk_ui_manager_add_ui (window->ui_manager, merge_id,
+ "/main-menu/view-menu/color-scheme-menu/placeholder-color-scheme-items",
+ name, name, GTK_UI_MANAGER_MENUITEM, FALSE);
+
+ /* cleanup */
+ g_free (name);
+ }
+
+ /* cleanup the list */
+ g_slist_free (schemes);
+
+ /* unlock */
+ lock_menu_updates--;
+}
+
+
+
+static void
+mousepad_window_menu_languages (MousepadWindow *window)
+{
+ gint merge_id;
+ gchar *name, *section_path;
+ gchar section_name[64];
+ GtkAction *action;
+ GtkRadioAction *radio_action;
+ GSList *group = NULL;
+ GSList *sections, *sect_iter, *languages, *lang_iter;
+
+ lock_menu_updates++;
+
+ merge_id = gtk_ui_manager_new_merge_id (window->ui_manager);
+
+ /* add the none language directly under the filetype */
+ radio_action = gtk_radio_action_new ("language-none",
+ _("None"),
+ _("No filetype"),
+ NULL,
+ g_str_hash ("none"));
+ gtk_radio_action_set_group (radio_action, group);
+ group = gtk_radio_action_get_group (radio_action);
+ g_signal_connect (G_OBJECT (radio_action), "activate", G_CALLBACK (mousepad_window_action_language), window);
+ gtk_action_group_add_action_with_accel (window->action_group, GTK_ACTION (radio_action), "");
+
+ /* release the action */
+ g_object_unref (G_OBJECT (radio_action));
+
+ /* add the action to the go menu */
+ gtk_ui_manager_add_ui (window->ui_manager,
+ merge_id,
+ "/main-menu/document-menu/language-menu/placeholder-language-section-items",
+ "language-none",
+ "language-none",
+ GTK_UI_MANAGER_MENUITEM,
+ FALSE);
+
+ /* add a separator */
+ gtk_ui_manager_add_ui (window->ui_manager,
+ merge_id,
+ "/main-menu/document-menu/language-menu/placeholder-language-section-items",
+ "language-separator",
+ NULL,
+ GTK_UI_MANAGER_SEPARATOR,
+ FALSE);
+
+ sections = mousepad_util_language_sections_get_sorted ();
+
+ for (sect_iter = sections; sect_iter != NULL; sect_iter = g_slist_next (sect_iter))
+ {
+ languages = mousepad_util_languages_get_sorted_for_section (sect_iter->data);
+
+ /* make sure there are langs in the section, otherwise skip it */
+ if (!languages)
+ continue;
+ else if (!g_slist_length (languages))
+ {
+ g_slist_free (languages);
+ continue;
+ }
+
+ g_snprintf (section_name, 64, "language-section-%s", (gchar *)sect_iter->data);
+
+ /* add the section directly under the gtkuimanager dynamic menu filetype */
+ action = gtk_action_new (section_name,
+ sect_iter->data,
+ NULL,
+ NULL);
+ gtk_action_group_add_action_with_accel (window->action_group, GTK_ACTION (action), "");
+
+ /* release the action */
+ g_object_unref (G_OBJECT (action));
+
+ /* add a menu for each section */
+ gtk_ui_manager_add_ui (window->ui_manager,
+ merge_id,
+ "/main-menu/document-menu/language-menu/placeholder-language-section-items",
+ section_name,
+ section_name,
+ GTK_UI_MANAGER_MENU,
+ FALSE);
+
+ section_path = g_strdup_printf ("/main-menu/document-menu/language-menu/"
+ "placeholder-language-section-items/%s",
+ section_name);
+
+ for (lang_iter = languages; lang_iter != NULL; lang_iter = g_slist_next (lang_iter))
+ {
+ if (g_strcmp0 (sect_iter->data, gtk_source_language_get_section (lang_iter->data)) == 0)
+ {
+ /* create action name */
+ name = g_strdup_printf ("language-%s", gtk_source_language_get_id (lang_iter->data));
+
+ radio_action = gtk_radio_action_new (name,
+ gtk_source_language_get_name (lang_iter->data),
+ NULL,
+ NULL,
+ g_str_hash (gtk_source_language_get_id (lang_iter->data)));
+ gtk_radio_action_set_group (radio_action, group);
+ group = gtk_radio_action_get_group (radio_action);
+ g_signal_connect (G_OBJECT (radio_action),
+ "activate",
+ G_CALLBACK (mousepad_window_action_language),
+ window);
+ gtk_action_group_add_action_with_accel (window->action_group, GTK_ACTION (radio_action), "");
+
+ /* release the action */
+ g_object_unref (G_OBJECT (radio_action));
+
+ /* add the action to the section menu */
+ gtk_ui_manager_add_ui (window->ui_manager,
+ merge_id,
+ section_path,
+ name,
+ name,
+ GTK_UI_MANAGER_MENUITEM,
+ FALSE);
+
+ /* cleanup before next language */
+ g_free (name);
+ }
+ }
+
+ /* cleanup before next section */
+ g_free (section_path);
+ g_slist_free (languages);
+ }
+
+ g_slist_free (sections);
+
+ /* unlock */
+ lock_menu_updates--;
+}
+
+
+
+/**
+ * Menu Actions
+ *
+ * All those function should be sorted by the menu structure so it's
+ * easy to find a function. The function can always use window->active, since
+ * we can assume there is always an active document inside a window.
+ **/
+static void
+mousepad_window_action_new (GtkAction *action,
+ MousepadWindow *window)
+{
+ MousepadDocument *document;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+
+ /* create new document */
+ document = mousepad_document_new ();
+
+ /* add the document to the window */
+ mousepad_window_add (window, document);
+}
+
+
+
+static void
+mousepad_window_action_new_window (GtkAction *action,
+ MousepadWindow *window)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+
+ /* emit the new window signal */
+ g_signal_emit (G_OBJECT (window), window_signals[NEW_WINDOW], 0);
+}
+
+
+
+static void
+mousepad_window_action_new_from_template (GtkMenuItem *item,
+ MousepadWindow *window)
+{
+ const gchar *filename;
+ GError *error = NULL;
+ gint result;
+ MousepadDocument *document;
+ const gchar *message;
+
+
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (GTK_IS_MENU_ITEM (item));
+
+ /* get the filename from the menu item */
+ filename = mousepad_object_get_data (G_OBJECT (item), "filename");
+
+ /* test if the file exists */
+ if (G_LIKELY (filename))
+ {
+ /* create new document */
+ document = mousepad_document_new ();
+
+ /* sink floating object */
+ g_object_ref_sink (G_OBJECT (document));
+
+ /* lock the undo manager */
+ gtk_source_buffer_begin_not_undoable_action (GTK_SOURCE_BUFFER (document->buffer));
+
+ /* try to load the template into the buffer */
+ result = mousepad_file_open (document->file, filename, &error);
+
+ /* release the lock */
+ gtk_source_buffer_end_not_undoable_action (GTK_SOURCE_BUFFER (document->buffer));
+
+ /* handle the result */
+ if (G_LIKELY (result == 0))
+ {
+ /* no errors, insert the document */
+ mousepad_window_add (window, document);
+ }
+ else
+ {
+ /* release the document */
+ g_object_unref (G_OBJECT (document));
+
+ /* handle the error */
+ switch (result)
+ {
+ case ERROR_NOT_UTF8_VALID:
+ case ERROR_CONVERTING_FAILED:
+ /* set error message */
+ message = _("Templates should be UTF-8 valid");
+ break;
+
+ case ERROR_READING_FAILED:
+ /* destroy the menu item */
+ gtk_widget_destroy (GTK_WIDGET (item));
+
+ /* set error message */
+ message = _("Reading the template failed, the menu item has been removed");
+ break;
+
+ default:
+ /* set error message */
+ message = _("Loading the template failed");
+ break;
+ }
+
+ /* show the error */
+ mousepad_dialogs_show_error (GTK_WINDOW (window), error, message);
+ }
+ }
+}
+
+
+
+static void
+mousepad_window_action_open (GtkAction *action,
+ MousepadWindow *window)
+{
+ GtkWidget *chooser;
+ GtkWidget *hbox, *label, *combobox;
+ const gchar *filename;
+ GSList *filenames, *li;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+
+ /* create new file chooser dialog */
+ chooser = gtk_file_chooser_dialog_new (_("Open File"),
+ GTK_WINDOW (window),
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
+ NULL);
+ gtk_dialog_set_default_response (GTK_DIALOG (chooser), GTK_RESPONSE_ACCEPT);
+ gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (chooser), TRUE);
+ gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (chooser), TRUE);
+
+ /* encoding selector */
+ hbox = gtk_hbox_new (FALSE, 6);
+ gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
+ gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (chooser), hbox);
+ gtk_widget_show (hbox);
+
+ label = gtk_label_new_with_mnemonic ("_Encoding:");
+ gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
+ gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
+ gtk_widget_show (label);
+
+ combobox = gtk_combo_box_new ();
+ gtk_box_pack_start (GTK_BOX (hbox), combobox, FALSE, FALSE, 0);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), combobox);
+ gtk_widget_show (combobox);
+
+ /* select the active document in the file chooser */
+ filename = mousepad_file_get_filename (window->active->file);
+ if (filename && g_file_test (filename, G_FILE_TEST_EXISTS))
+ gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (chooser), filename);
+
+ /* run the dialog */
+ if (G_LIKELY (gtk_dialog_run (GTK_DIALOG (chooser)) == GTK_RESPONSE_ACCEPT))
+ {
+ /* hide the dialog */
+ gtk_widget_hide (chooser);
+
+ /* get a list of selected filenames */
+ filenames = gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER (chooser));
+
+ /* lock menu updates */
+ lock_menu_updates++;
+
+ /* open all the selected filenames in a new tab */
+ for (li = filenames; li != NULL; li = li->next)
+ {
+ /* open the file */
+ mousepad_window_open_file (window, li->data, MOUSEPAD_ENCODING_UTF_8);
+
+ /* cleanup */
+ g_free (li->data);
+ }
+
+ /* cleanup */
+ g_slist_free (filenames);
+
+ /* allow menu updates again */
+ lock_menu_updates--;
+
+ /* update the menus */
+ mousepad_window_recent_menu (window);
+ mousepad_window_update_gomenu (window);
+ }
+
+ /* destroy dialog */
+ gtk_widget_destroy (chooser);
+}
+
+
+
+static void
+mousepad_window_action_open_recent (GtkAction *action,
+ MousepadWindow *window)
+{
+ const gchar *uri, *charset;
+ MousepadEncoding encoding;
+ GError *error = NULL;
+ gchar *filename;
+ gboolean succeed = FALSE;
+ GtkRecentInfo *info;
+
+ mousepad_return_if_fail (GTK_IS_ACTION (action));
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+
+ /* get the info */
+ info = mousepad_object_get_data (G_OBJECT (action), "gtk-recent-info");
+
+ if (G_LIKELY (info != NULL))
+ {
+ /* get the file uri */
+ uri = gtk_recent_info_get_uri (info);
+
+ /* build a filename from the uri */
+ filename = g_filename_from_uri (uri, NULL, NULL);
+
+ if (G_LIKELY (filename != NULL))
+ {
+ /* open the file in a new tab if it exists */
+ if (g_file_test (filename, G_FILE_TEST_EXISTS))
+ {
+ /* try to get the charset from the description */
+ charset = mousepad_window_recent_get_charset (info);
+
+ /* lookup the encoding */
+ encoding = mousepad_encoding_find (charset);
+
+ /* try to open the file */
+ succeed = mousepad_window_open_file (window, filename, encoding);
+ }
+ else
+ {
+ /* create an error */
+ g_set_error (&error, G_FILE_ERROR, G_FILE_ERROR_IO,
+ _("Failed to open \"%s\" for reading. It will be "
+ "removed from the document history"), filename);
+
+ /* show the warning and cleanup */
+ mousepad_dialogs_show_error (GTK_WINDOW (window), error, _("Failed to open file"));
+ g_error_free (error);
+ }
+
+ /* cleanup */
+ g_free (filename);
+
+ /* update the document history */
+ if (G_LIKELY (succeed))
+ gtk_recent_manager_add_item (window->recent_manager, uri);
+ else
+ gtk_recent_manager_remove_item (window->recent_manager, uri, NULL);
+ }
+ }
+}
+
+
+
+static void
+mousepad_window_action_clear_recent (GtkAction *action,
+ MousepadWindow *window)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+
+ /* ask the user if he or she really want to clear the history */
+ if (mousepad_dialogs_clear_recent (GTK_WINDOW (window)))
+ {
+ /* avoid updating the menu */
+ lock_menu_updates++;
+
+ /* clear the document history */
+ mousepad_window_recent_clear (window);
+
+ /* allow menu updates again */
+ lock_menu_updates--;
+
+ /* update the recent menu */
+ mousepad_window_recent_menu (window);
+ }
+}
+
+
+
+static gboolean
+mousepad_window_action_save (GtkAction *action,
+ MousepadWindow *window)
+{
+ MousepadDocument *document = window->active;
+ GError *error = NULL;
+ gboolean succeed = FALSE;
+ gboolean modified;
+ gint response;
+
+ mousepad_return_val_if_fail (MOUSEPAD_IS_WINDOW (window), FALSE);
+ mousepad_return_val_if_fail (MOUSEPAD_IS_DOCUMENT (window->active), FALSE);
+
+ if (mousepad_file_get_filename (document->file) == NULL)
+ {
+ /* file has no filename yet, open the save as dialog */
+ succeed = mousepad_window_action_save_as (NULL, window);
+ }
+ else
+ {
+ /* check whether the file is externally modified */
+ modified = mousepad_file_get_externally_modified (document->file, &error);
+ if (G_UNLIKELY (error != NULL))
+ goto showerror;
+
+ if (modified)
+ {
+ /* ask the user what to do */
+ response = mousepad_dialogs_externally_modified (GTK_WINDOW (window));
+ }
+ else
+ {
+ /* save */
+ response = MOUSEPAD_RESPONSE_SAVE;
+ }
+
+ switch (response)
+ {
+ case MOUSEPAD_RESPONSE_CANCEL:
+ /* do nothing */
+ return FALSE;
+
+ case MOUSEPAD_RESPONSE_SAVE_AS:
+ /* run save as dialog */
+ succeed = mousepad_window_action_save_as (NULL, window);
+ break;
+
+ case MOUSEPAD_RESPONSE_SAVE:
+ /* save the document */
+ succeed = mousepad_file_save (document->file, &error);
+ break;
+ }
+
+ if (G_LIKELY (succeed))
+ {
+ /* update the window title */
+ mousepad_window_set_title (window);
+ }
+ else if (error != NULL)
+ {
+ showerror:
+
+ /* show the error */
+ mousepad_dialogs_show_error (GTK_WINDOW (window), error, _("Failed to save the document"));
+ g_error_free (error);
+ }
+ }
+
+ return succeed;
+}
+
+
+
+static gboolean
+mousepad_window_action_save_as (GtkAction *action,
+ MousepadWindow *window)
+{
+ MousepadDocument *document = window->active;
+ gchar *filename;
+ const gchar *current_filename;
+ GtkWidget *dialog;
+ gboolean succeed = FALSE;
+
+ mousepad_return_val_if_fail (MOUSEPAD_IS_WINDOW (window), FALSE);
+ mousepad_return_val_if_fail (MOUSEPAD_IS_DOCUMENT (window->active), FALSE);
+
+ /* create the dialog */
+ dialog = gtk_file_chooser_dialog_new (_("Save As"),
+ GTK_WINDOW (window), GTK_FILE_CHOOSER_ACTION_SAVE,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_SAVE, GTK_RESPONSE_OK, NULL);
+ gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (dialog), TRUE);
+ gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), TRUE);
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+
+ /* set the current filename if there is one */
+ current_filename = mousepad_file_get_filename (document->file);
+ if (current_filename)
+ gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (dialog), current_filename);
+
+ /* run the dialog */
+ if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK)
+ {
+ /* get the new filename */
+ filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
+
+ if (G_LIKELY (filename))
+ {
+ /* set the new filename */
+ mousepad_file_set_filename (document->file, filename);
+
+ /* cleanup */
+ g_free (filename);
+
+ /* save the file with the function above */
+ succeed = mousepad_window_action_save (NULL, window);
+
+ /* add to the recent history is saving succeeded */
+ if (G_LIKELY (succeed))
+ mousepad_window_recent_add (window, document->file);
+ }
+ }
+
+ /* destroy the dialog */
+ gtk_widget_destroy (dialog);
+
+ return succeed;
+}
+
+
+
+static void
+mousepad_window_action_save_all (GtkAction *action,
+ MousepadWindow *window)
+{
+ gint i, current;
+ gint page_num;
+ MousepadDocument *document;
+ GSList *li, *documents = NULL;
+ gboolean succeed = TRUE;
+ GError *error = NULL;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+
+ /* get the current active tab */
+ current = gtk_notebook_get_current_page (GTK_NOTEBOOK (window->notebook));
+
+ /* walk though all the document in the window */
+ for (i = 0; i < gtk_notebook_get_n_pages (GTK_NOTEBOOK (window->notebook)); i++)
+ {
+ /* get the document */
+ document = MOUSEPAD_DOCUMENT (gtk_notebook_get_nth_page (GTK_NOTEBOOK (window->notebook), i));
+
+ /* debug check */
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (document));
+
+ /* continue if the document is not modified */
+ if (!gtk_text_buffer_get_modified (document->buffer))
+ continue;
+
+ /* we try to quickly save files, without bothering the user */
+ if (mousepad_file_get_filename (document->file) != NULL
+ && mousepad_file_get_read_only (document->file) == FALSE
+ && mousepad_file_get_externally_modified (document->file, NULL) == FALSE)
+ {
+ /* try to quickly save the file */
+ succeed = mousepad_file_save (document->file, &error);
+
+ /* break on problems */
+ if (G_UNLIKELY (!succeed))
+ break;
+ }
+ else
+ {
+ /* add the document to a queue to bother the user later */
+ documents = g_slist_prepend (documents, document);
+ }
+ }
+
+ if (G_UNLIKELY (succeed == FALSE))
+ {
+ /* focus the tab that triggered the problem */
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (window->notebook), i);
+
+ /* show the error */
+ mousepad_dialogs_show_error (GTK_WINDOW (window), error, _("Failed to save the document"));
+
+ /* free error */
+ if (error != NULL)
+ g_error_free (error);
+ }
+ else
+ {
+ /* open a save as dialog for all the unnamed files */
+ for (li = documents; li != NULL; li = li->next)
+ {
+ document = MOUSEPAD_DOCUMENT (li->data);
+
+ /* get the documents page number */
+ page_num = gtk_notebook_page_num (GTK_NOTEBOOK (window->notebook), GTK_WIDGET (li->data));
+
+ if (G_LIKELY (page_num > -1))
+ {
+ /* focus the tab we're going to save */
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (window->notebook), page_num);
+
+ if (mousepad_file_get_filename (document->file) == NULL
+ || mousepad_file_get_read_only (document->file))
+ {
+ /* trigger the save as function */
+ mousepad_window_action_save_as (NULL, window);
+ }
+ else
+ {
+ /* trigger the save function (externally modified document) */
+ mousepad_window_action_save (NULL, window);
+ }
+ }
+ }
+
+ /* focus the origional doc if everything went fine */
+ if (G_LIKELY (li == NULL))
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (window->notebook), current);
+ }
+
+ /* cleanup */
+ g_slist_free (documents);
+}
+
+
+
+static void
+mousepad_window_action_revert (GtkAction *action,
+ MousepadWindow *window)
+{
+ MousepadDocument *document = window->active;
+ GError *error = NULL;
+ gint response;
+ gboolean succeed;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+
+ /* ask the user if he really wants to do this when the file is modified */
+ if (gtk_text_buffer_get_modified (document->buffer))
+ {
+ /* ask the user if he really wants to revert */
+ response = mousepad_dialogs_revert (GTK_WINDOW (window));
+
+ if (response == MOUSEPAD_RESPONSE_SAVE_AS)
+ {
+ /* open the save as dialog, leave when use user did not save (or it failed) */
+ if (!mousepad_window_action_save_as (NULL, window))
+ return;
+ }
+ else if (response == MOUSEPAD_RESPONSE_CANCEL)
+ {
+ /* meh, first click revert and then cancel... pussy... */
+ return;
+ }
+
+ /* small check for debug builds */
+ mousepad_return_if_fail (response == MOUSEPAD_RESPONSE_REVERT);
+ }
+
+ /* lock the undo manager */
+ gtk_source_buffer_begin_not_undoable_action (GTK_SOURCE_BUFFER (document->buffer));
+
+ /* reload the file */
+ succeed = mousepad_file_reload (document->file, &error);
+
+ /* release the lock */
+ gtk_source_buffer_end_not_undoable_action (GTK_SOURCE_BUFFER (document->buffer));
+
+ if (G_UNLIKELY (succeed == FALSE))
+ {
+ /* show the error */
+ mousepad_dialogs_show_error (GTK_WINDOW (window), error, _("Failed to reload the document"));
+ g_error_free (error);
+ }
+}
+
+
+
+static void
+mousepad_window_action_print (GtkAction *action,
+ MousepadWindow *window)
+{
+ MousepadPrint *print;
+ GError *error = NULL;
+ gboolean succeed;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+
+ /* create new print operation */
+ print = mousepad_print_new ();
+
+ /* print the current document */
+ succeed = mousepad_print_document_interactive (print, window->active, GTK_WINDOW (window), &error);
+
+ if (G_UNLIKELY (succeed == FALSE))
+ {
+ /* show the error */
+ mousepad_dialogs_show_error (GTK_WINDOW (window), error, _("Failed to print the document"));
+ g_error_free (error);
+ }
+
+ /* release the object */
+ g_object_unref (G_OBJECT (print));
+}
+
+
+
+static void
+mousepad_window_action_detach (GtkAction *action,
+ MousepadWindow *window)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+
+#if GTK_CHECK_VERSION (2,12,0)
+ /* invoke function without cooridinates */
+ mousepad_window_notebook_create_window (GTK_NOTEBOOK (window->notebook),
+ GTK_WIDGET (window->active),
+ -1, -1, window);
+#else
+ /* only detach when there are more then 2 tabs */
+ if (G_LIKELY (gtk_notebook_get_n_pages (GTK_NOTEBOOK (window->notebook)) >= 2))
+ {
+ /* take a reference */
+ g_object_ref (G_OBJECT (window->active));
+
+ /* remove the document from the active window */
+ gtk_container_remove (GTK_CONTAINER (window->notebook), GTK_WIDGET (window->active));
+
+ /* emit the new window with document signal */
+ g_signal_emit (G_OBJECT (window), window_signals[NEW_WINDOW_WITH_DOCUMENT], 0, window->active, -1, -1);
+
+ /* release our reference */
+ g_object_unref (G_OBJECT (window->active));
+ }
+#endif
+}
+
+
+
+static void
+mousepad_window_action_close (GtkAction *action,
+ MousepadWindow *window)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+
+ /* close active document */
+ mousepad_window_close_document (window, window->active);
+}
+
+
+
+static void
+mousepad_window_action_close_window (GtkAction *action,
+ MousepadWindow *window)
+{
+ gint npages, i;
+ GtkWidget *document;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+
+ /* get the number of page in the notebook */
+ npages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (window->notebook)) - 1;
+
+ /* prevent menu updates */
+ lock_menu_updates++;
+
+ /* ask what to do with the modified document in this window */
+ for (i = npages; i >= 0; --i)
+ {
+ /* get the document */
+ document = gtk_notebook_get_nth_page (GTK_NOTEBOOK (window->notebook), i);
+
+ /* check for debug builds */
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (document));
+
+ /* focus the tab we're going to close */
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (window->notebook), i);
+
+ /* close each document */
+ if (!mousepad_window_close_document (window, MOUSEPAD_DOCUMENT (document)))
+ {
+ /* closing cancelled, release menu lock */
+ lock_menu_updates--;
+
+ /* rebuild go menu */
+ mousepad_window_update_gomenu (window);
+
+ /* leave function */
+ return;
+ }
+ }
+
+ /* release lock */
+ lock_menu_updates--;
+}
+
+
+
+static void
+mousepad_window_action_undo (GtkAction *action,
+ MousepadWindow *window)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+
+ /* undo */
+ gtk_source_buffer_undo (GTK_SOURCE_BUFFER (window->active->buffer));
+
+ /* scroll to visible area */
+ mousepad_view_scroll_to_cursor (window->active->textview);
+}
+
+
+
+static void
+mousepad_window_action_redo (GtkAction *action,
+ MousepadWindow *window)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+
+ /* redo */
+ gtk_source_buffer_redo (GTK_SOURCE_BUFFER (window->active->buffer));
+
+ /* scroll to visible area */
+ mousepad_view_scroll_to_cursor (window->active->textview);
+}
+
+
+
+static void
+mousepad_window_action_cut (GtkAction *action,
+ MousepadWindow *window)
+{
+ GtkEditable *entry;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+
+ /* get searchbar entry */
+ entry = mousepad_search_bar_entry (MOUSEPAD_SEARCH_BAR (window->search_bar));
+
+ /* cut from search bar entry or textview */
+ if (G_UNLIKELY (entry))
+ gtk_editable_cut_clipboard (entry);
+ else
+ mousepad_view_clipboard_cut (window->active->textview);
+
+ /* update the history */
+ mousepad_window_paste_history_add (window);
+}
+
+
+
+static void
+mousepad_window_action_copy (GtkAction *action,
+ MousepadWindow *window)
+{
+ GtkEditable *entry;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+
+ /* get searchbar entry */
+ entry = mousepad_search_bar_entry (MOUSEPAD_SEARCH_BAR (window->search_bar));
+
+ /* copy from search bar entry or textview */
+ if (G_UNLIKELY (entry))
+ gtk_editable_copy_clipboard (entry);
+ else
+ mousepad_view_clipboard_copy (window->active->textview);
+
+ /* update the history */
+ mousepad_window_paste_history_add (window);
+}
+
+
+
+static void
+mousepad_window_action_paste (GtkAction *action,
+ MousepadWindow *window)
+{
+ GtkEditable *entry;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+
+ /* get searchbar entry */
+ entry = mousepad_search_bar_entry (MOUSEPAD_SEARCH_BAR (window->search_bar));
+
+ /* paste in search bar entry or textview */
+ if (G_UNLIKELY (entry))
+ gtk_editable_paste_clipboard (entry);
+ else
+ mousepad_view_clipboard_paste (window->active->textview, NULL, FALSE);
+}
+
+
+
+static void
+mousepad_window_action_paste_history (GtkAction *action,
+ MousepadWindow *window)
+{
+ GtkWidget *menu;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+
+ /* get the history menu */
+ menu = mousepad_window_paste_history_menu (window);
+
+ /* select the first item in the menu */
+ gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), TRUE);
+
+ /* popup the menu */
+ gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
+ mousepad_window_paste_history_menu_position,
+ window, 0, gtk_get_current_event_time ());
+}
+
+
+
+static void
+mousepad_window_action_paste_column (GtkAction *action,
+ MousepadWindow *window)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+
+ /* paste the clipboard into a column */
+ mousepad_view_clipboard_paste (window->active->textview, NULL, TRUE);
+}
+
+
+
+static void
+mousepad_window_action_delete (GtkAction *action,
+ MousepadWindow *window)
+{
+ GtkEditable *entry;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+
+ /* get searchbar entry */
+ entry = mousepad_search_bar_entry (MOUSEPAD_SEARCH_BAR (window->search_bar));
+
+ /* delete selection in search bar entry or textview */
+ if (G_UNLIKELY (entry))
+ gtk_editable_delete_selection (entry);
+ else
+ mousepad_view_delete_selection (window->active->textview);
+}
+
+
+
+static void
+mousepad_window_action_select_all (GtkAction *action,
+ MousepadWindow *window)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+
+ /* select everything in the document */
+ mousepad_view_select_all (window->active->textview);
+}
+
+
+
+static void
+mousepad_window_action_change_selection (GtkAction *action,
+ MousepadWindow *window)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+
+ /* change the selection */
+ mousepad_view_change_selection (window->active->textview);
+}
+
+
+
+static void
+mousepad_window_action_find (GtkAction *action,
+ MousepadWindow *window)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+
+ /* create a new search bar is needed */
+ if (window->search_bar == NULL)
+ {
+ /* create a new toolbar and pack it into the box */
+ window->search_bar = mousepad_search_bar_new ();
+ gtk_box_pack_start (GTK_BOX (window->box), window->search_bar, FALSE, FALSE, PADDING);
+
+ /* connect signals */
+ g_signal_connect_swapped (G_OBJECT (window->search_bar), "hide-bar", G_CALLBACK (mousepad_window_hide_search_bar), window);
+ g_signal_connect_swapped (G_OBJECT (window->search_bar), "search", G_CALLBACK (mousepad_window_search), window);
+ }
+
+ /* show the search bar */
+ gtk_widget_show (window->search_bar);
+
+ /* focus the search entry */
+ mousepad_search_bar_focus (MOUSEPAD_SEARCH_BAR (window->search_bar));
+}
+
+
+
+static void
+mousepad_window_action_find_next (GtkAction *action,
+ MousepadWindow *window)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+
+ /* find the next occurence */
+ if (G_LIKELY (window->search_bar != NULL))
+ mousepad_search_bar_find_next (MOUSEPAD_SEARCH_BAR (window->search_bar));
+}
+
+
+
+static void
+mousepad_window_action_find_previous (GtkAction *action,
+ MousepadWindow *window)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+
+ /* find the previous occurence */
+ if (G_LIKELY (window->search_bar != NULL))
+ mousepad_search_bar_find_previous (MOUSEPAD_SEARCH_BAR (window->search_bar));
+}
+
+
+static void
+mousepad_window_action_replace_switch_page (MousepadWindow *window)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_REPLACE_DIALOG (window->replace_dialog));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+
+ /* page switched */
+ mousepad_replace_dialog_page_switched (MOUSEPAD_REPLACE_DIALOG (window->replace_dialog));
+}
+
+
+static void
+mousepad_window_action_replace_destroy (MousepadWindow *window)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+
+ /* disconnect tab switch signal */
+ g_signal_handlers_disconnect_by_func (G_OBJECT (window->notebook), mousepad_window_action_replace_switch_page, window);
+
+ /* reset the dialog variable */
+ window->replace_dialog = NULL;
+}
+
+
+static void
+mousepad_window_action_replace (GtkAction *action,
+ MousepadWindow *window)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+
+ if (window->replace_dialog == NULL)
+ {
+ /* create a new dialog */
+ window->replace_dialog = mousepad_replace_dialog_new ();
+
+ /* popup the dialog */
+ gtk_window_set_destroy_with_parent (GTK_WINDOW (window->replace_dialog), TRUE);
+ gtk_window_set_transient_for (GTK_WINDOW (window->replace_dialog), GTK_WINDOW (window));
+ gtk_widget_show (window->replace_dialog);
+
+ /* connect signals */
+ g_signal_connect_swapped (G_OBJECT (window->replace_dialog), "destroy", G_CALLBACK (mousepad_window_action_replace_destroy), window);
+ g_signal_connect_swapped (G_OBJECT (window->replace_dialog), "search", G_CALLBACK (mousepad_window_search), window);
+ g_signal_connect_swapped (G_OBJECT (window->notebook), "switch-page", G_CALLBACK (mousepad_window_action_replace_switch_page), window);
+ }
+ else
+ {
+ /* focus the existing dialog */
+ gtk_window_present (GTK_WINDOW (window->replace_dialog));
+ }
+}
+
+
+
+static void
+mousepad_window_action_select_font (GtkAction *action,
+ MousepadWindow *window)
+{
+ GtkWidget *dialog;
+ MousepadDocument *document;
+ gchar *font_name;
+ gint i;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+
+ dialog = gtk_font_selection_dialog_new (_("Choose Mousepad Font"));
+ gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (window));
+
+ /* set the current font name */
+ g_object_get (G_OBJECT (window->preferences), "view-font-name", &font_name, NULL);
+ if (G_LIKELY (font_name))
+ {
+ gtk_font_selection_dialog_set_font_name (GTK_FONT_SELECTION_DIALOG (dialog), font_name);
+ g_free (font_name);
+ }
+
+ /* run the dialog */
+ if (G_LIKELY (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK))
+ {
+ /* get the selected font from the dialog */
+ font_name = gtk_font_selection_dialog_get_font_name (GTK_FONT_SELECTION_DIALOG (dialog));
+
+ /* store the font in the preferences */
+ g_object_set (G_OBJECT (window->preferences), "view-font-name", font_name, NULL);
+
+ /* apply the font in all documents in this window */
+ for (i = 0; i < gtk_notebook_get_n_pages (GTK_NOTEBOOK (window->notebook)); i++)
+ {
+ /* get the document */
+ document = MOUSEPAD_DOCUMENT (gtk_notebook_get_nth_page (GTK_NOTEBOOK (window->notebook), i));
+
+ /* debug check */
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (document));
+
+ /* set the font */
+ mousepad_document_set_font (document, font_name);
+
+ /* update the tab array */
+ mousepad_view_set_tab_size (document->textview, mousepad_view_get_tab_size (document->textview));
+ }
+
+ /* cleanup */
+ g_free (font_name);
+ }
+
+ /* destroy dialog */
+ gtk_widget_destroy (dialog);
+}
+
+
+
+static void
+mousepad_window_action_color_scheme (GtkToggleAction *action,
+ MousepadWindow *window)
+{
+ gint page_num = 0;
+ guint scheme_id_hash;
+ GtkWidget *page;
+ GtkTextBuffer *buffer;
+ GtkSourceStyleScheme *scheme = NULL;
+ MousepadDocument *document;
+ GSList *schemes, *iter;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+
+ /* leave when menu updates are locked */
+ if (lock_menu_updates == 0 && gtk_toggle_action_get_active (action))
+ {
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+
+ /* get the color scheme id hashed */
+ scheme_id_hash = (guint) gtk_radio_action_get_current_value (GTK_RADIO_ACTION (action));
+
+ if (scheme_id_hash != g_str_hash ("none"))
+ {
+ /* lookup the scheme from the id hash */
+ schemes = mousepad_util_color_schemes_get ();
+ for (iter = schemes; iter != NULL; iter = g_slist_next (iter))
+ {
+ if (scheme_id_hash == g_str_hash (gtk_source_style_scheme_get_id (iter->data)))
+ {
+ scheme = iter->data;
+ break;
+ }
+ }
+ g_slist_free (schemes);
+ }
+
+ /* store as last used value */
+ g_object_set (G_OBJECT (window->preferences),
+ "view-color-scheme",
+ (scheme != NULL) ? gtk_source_style_scheme_get_id (scheme) : "none",
+ NULL);
+
+ /* apply colour scheme to all open textviews */
+ while ((page = gtk_notebook_get_nth_page (GTK_NOTEBOOK (window->notebook), page_num)))
+ {
+ if (G_LIKELY (MOUSEPAD_IS_DOCUMENT (page)))
+ {
+ document = MOUSEPAD_DOCUMENT (page);
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (document->textview));
+ gtk_source_buffer_set_highlight_syntax (GTK_SOURCE_BUFFER (buffer), (scheme != NULL));
+ gtk_source_buffer_set_style_scheme (GTK_SOURCE_BUFFER (buffer), scheme);
+ }
+ page_num++;
+ }
+ }
+}
+
+
+
+static void
+mousepad_window_action_line_numbers (GtkToggleAction *action,
+ MousepadWindow *window)
+{
+ gint page_num = 0;
+ gboolean active;
+ GtkWidget *page;
+ MousepadDocument *document;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+
+ /* leave when menu updates are locked */
+ if (lock_menu_updates == 0)
+ {
+ /* get the current state */
+ active = gtk_toggle_action_get_active (action);
+
+ /* save as the last used line number setting */
+ g_object_set (G_OBJECT (window->preferences), "view-line-numbers", active, NULL);
+
+ /* apply line numbers setting to all open textviews */
+ while ((page = gtk_notebook_get_nth_page (GTK_NOTEBOOK (window->notebook), page_num)))
+ {
+ if (G_LIKELY (MOUSEPAD_IS_DOCUMENT (page)))
+ {
+ document = MOUSEPAD_DOCUMENT (page);
+ mousepad_view_set_line_numbers (document->textview, active);
+ }
+ page_num++;
+ }
+ }
+}
+
+
+
+static void
+mousepad_window_action_statusbar_overwrite (MousepadWindow *window,
+ gboolean overwrite)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+
+ /* set the new overwrite mode */
+ mousepad_document_set_overwrite (window->active, overwrite);
+}
+
+
+
+static void
+mousepad_window_action_statusbar (GtkToggleAction *action,
+ MousepadWindow *window)
+{
+ gboolean show_statusbar;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+
+ /* whether we show the statusbar */
+ show_statusbar = gtk_toggle_action_get_active (action);
+
+ /* check if we should drop the statusbar */
+ if (!show_statusbar && window->statusbar != NULL)
+ {
+ /* destroy the statusbar */
+ gtk_widget_destroy (window->statusbar);
+ window->statusbar = NULL;
+ }
+ else if (show_statusbar && window->statusbar == NULL)
+ {
+ /* setup a new statusbar */
+ window->statusbar = mousepad_statusbar_new ();
+ gtk_box_pack_end (GTK_BOX (window->box), window->statusbar, FALSE, FALSE, 0);
+ gtk_widget_show (window->statusbar);
+
+ /* overwrite toggle signal */
+ g_signal_connect_swapped (G_OBJECT (window->statusbar), "enable-overwrite",
+ G_CALLBACK (mousepad_window_action_statusbar_overwrite), window);
+
+ /* populate filetype popup menu signal */
+ g_signal_connect_swapped (G_OBJECT (window->statusbar), "populate-filetype-popup",
+ G_CALLBACK (mousepad_window_populate_statusbar_popup), window);
+
+ /* update the statusbar items */
+ if (window->active)
+ {
+ /* debug check */
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+
+ /* ask document to resend the cursor status signals */
+ mousepad_document_send_signals (window->active);
+ }
+ }
+
+ /* remember the setting */
+ g_object_set (G_OBJECT (window->preferences), "window-statusbar-visible", show_statusbar, NULL);
+}
+
+
+
+static void
+mousepad_window_action_lowercase (GtkAction *action,
+ MousepadWindow *window)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+
+ /* convert selection to lowercase */
+ mousepad_view_convert_selection_case (window->active->textview, LOWERCASE);
+}
+
+
+
+static void
+mousepad_window_action_uppercase (GtkAction *action,
+ MousepadWindow *window)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+
+ /* convert selection to uppercase */
+ mousepad_view_convert_selection_case (window->active->textview, UPPERCASE);
+}
+
+
+
+static void
+mousepad_window_action_titlecase (GtkAction *action,
+ MousepadWindow *window)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+
+ /* convert selection to titlecase */
+ mousepad_view_convert_selection_case (window->active->textview, TITLECASE);
+}
+
+
+
+static void
+mousepad_window_action_opposite_case (GtkAction *action,
+ MousepadWindow *window)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+
+ /* convert selection to opposite case */
+ mousepad_view_convert_selection_case (window->active->textview, OPPOSITE_CASE);
+}
+
+
+
+static void
+mousepad_window_action_tabs_to_spaces (GtkAction *action,
+ MousepadWindow *window)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+
+ /* convert tabs to spaces */
+ mousepad_view_convert_spaces_and_tabs (window->active->textview, TABS_TO_SPACES);
+}
+
+
+
+static void
+mousepad_window_action_spaces_to_tabs (GtkAction *action,
+ MousepadWindow *window)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+
+ /* convert spaces to tabs */
+ mousepad_view_convert_spaces_and_tabs (window->active->textview, SPACES_TO_TABS);
+}
+
+
+
+static void
+mousepad_window_action_strip_trailing_spaces (GtkAction *action,
+ MousepadWindow *window)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+
+ /* convert spaces to tabs */
+ mousepad_view_strip_trailing_spaces (window->active->textview);
+}
+
+
+
+static void
+mousepad_window_action_transpose (GtkAction *action,
+ MousepadWindow *window)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+
+ /* transpose */
+ mousepad_view_transpose (window->active->textview);
+}
+
+
+
+static void
+mousepad_window_action_move_line_up (GtkAction *action,
+ MousepadWindow *window)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+
+ /* move the selection on line up */
+ mousepad_view_move_selection (window->active->textview, MOVE_LINE_UP);
+}
+
+
+
+static void
+mousepad_window_action_move_line_down (GtkAction *action,
+ MousepadWindow *window)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+
+ /* move the selection on line down */
+ mousepad_view_move_selection (window->active->textview, MOVE_LINE_DOWN);
+}
+
+
+
+static void
+mousepad_window_action_duplicate (GtkAction *action,
+ MousepadWindow *window)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+
+ /* dupplicate */
+ mousepad_view_duplicate (window->active->textview);
+}
+
+
+
+static void
+mousepad_window_action_increase_indent (GtkAction *action,
+ MousepadWindow *window)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+
+ /* increase the indent */
+ mousepad_view_indent (window->active->textview, INCREASE_INDENT);
+}
+
+
+
+static void
+mousepad_window_action_decrease_indent (GtkAction *action,
+ MousepadWindow *window)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+
+ /* decrease the indent */
+ mousepad_view_indent (window->active->textview, DECREASE_INDENT);
+}
+
+
+
+static void
+mousepad_window_action_auto_indent (GtkToggleAction *action,
+ MousepadWindow *window)
+{
+ gboolean active;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+
+ /* leave when menu updates are locked */
+ if (lock_menu_updates == 0)
+ {
+ /* get the current state */
+ active = gtk_toggle_action_get_active (action);
+
+ /* save as the last auto indent mode */
+ g_object_set (G_OBJECT (window->preferences), "view-auto-indent", active, NULL);
+
+ /* update the active document */
+ mousepad_view_set_auto_indent (window->active->textview, active);
+ }
+}
+
+
+
+static void
+mousepad_window_action_line_ending (GtkRadioAction *action,
+ GtkRadioAction *current,
+ MousepadWindow *window)
+{
+ MousepadLineEnding eol;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+ mousepad_return_if_fail (MOUSEPAD_IS_FILE (window->active->file));
+ mousepad_return_if_fail (GTK_IS_TEXT_BUFFER (window->active->buffer));
+
+ /* leave when menu updates are locked */
+ if (lock_menu_updates == 0)
+ {
+ /* get selected line ending */
+ eol = gtk_radio_action_get_current_value (current);
+
+ /* set the new line ending on the file */
+ mousepad_file_set_line_ending (window->active->file, eol);
+
+ /* make buffer as modified to show the user the change is not saved */
+ gtk_text_buffer_set_modified (window->active->buffer, TRUE);
+ }
+}
+
+
+
+static void
+mousepad_window_action_tab_size (GtkToggleAction *action,
+ MousepadWindow *window)
+{
+ gboolean tab_size;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+
+ /* leave when menu updates are locked */
+ if (lock_menu_updates == 0 && gtk_toggle_action_get_active (action))
+ {
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+
+ /* get the tab size */
+ tab_size = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (action));
+
+ /* whether the other item was clicked */
+ if (tab_size == 0)
+ {
+ /* get tab size from document */
+ tab_size = mousepad_view_get_tab_size (window->active->textview);
+
+ /* select other size in dialog */
+ tab_size = mousepad_dialogs_other_tab_size (GTK_WINDOW (window), tab_size);
+ }
+
+ /* store as last used value */
+ g_object_set (G_OBJECT (window->preferences), "view-tab-size", tab_size, NULL);
+
+ /* set the value */
+ mousepad_view_set_tab_size (window->active->textview, tab_size);
+
+ /* update menu */
+ mousepad_window_menu_tab_sizes_update (window);
+ }
+}
+
+
+
+static void
+mousepad_window_action_word_wrap (GtkToggleAction *action,
+ MousepadWindow *window)
+{
+ gboolean active;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+
+ /* leave when menu updates are locked */
+ if (lock_menu_updates == 0)
+ {
+ /* get the current state */
+ active = gtk_toggle_action_get_active (action);
+
+ /* store this as the last used wrap mode */
+ g_object_set (G_OBJECT (window->preferences), "view-word-wrap", active, NULL);
+
+ /* set the wrapping mode of the current document */
+ mousepad_document_set_word_wrap (window->active, active);
+ }
+}
+
+
+
+static void
+mousepad_window_action_write_bom (GtkToggleAction *action,
+ MousepadWindow *window)
+{
+ gboolean active;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+
+ /* leave when menu updates are locked */
+ if (lock_menu_updates == 0)
+ {
+ /* get the current state */
+ active = gtk_toggle_action_get_active (action);
+
+ /* set new value */
+ mousepad_file_set_write_bom (window->active->file, active);
+
+ /* make buffer as modified to show the user the change is not saved */
+ gtk_text_buffer_set_modified (window->active->buffer, TRUE);
+ }
+}
+
+
+
+static void
+mousepad_window_action_language (GtkToggleAction *action,
+ MousepadWindow *window)
+{
+ guint lang_hash;
+ const gchar *const *lang_id;
+ GtkSourceLanguage *language;
+ GtkSourceLanguageManager *manager;
+ GtkSourceBuffer *buffer;
+
+ lang_hash = (guint) gtk_radio_action_get_current_value (GTK_RADIO_ACTION (action));
+ buffer = GTK_SOURCE_BUFFER (gtk_text_view_get_buffer (GTK_TEXT_VIEW (window->active->textview)));
+
+ if (lang_hash == g_str_hash ("none"))
+ {
+ gtk_source_buffer_set_language (buffer, NULL);
+ return;
+ }
+
+ manager = gtk_source_language_manager_get_default ();
+ lang_id = gtk_source_language_manager_get_language_ids (manager);
+
+ while (*lang_id)
+ {
+ if (g_str_hash (*lang_id) == lang_hash)
+ {
+ language = gtk_source_language_manager_get_language (manager, *lang_id);
+ gtk_source_buffer_set_language (buffer, language);
+ break;
+ }
+ lang_id++;
+ }
+}
+
+
+
+static void
+mousepad_window_action_insert_spaces (GtkToggleAction *action,
+ MousepadWindow *window)
+{
+ gboolean insert_spaces;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+
+ /* leave when menu updates are locked */
+ if (lock_menu_updates == 0)
+ {
+ /* get the current state */
+ insert_spaces = gtk_toggle_action_get_active (action);
+
+ /* save as the last auto indent mode */
+ g_object_set (G_OBJECT (window->preferences), "view-insert-spaces", insert_spaces, NULL);
+
+ /* update the active document */
+ mousepad_view_set_insert_spaces (window->active->textview, insert_spaces);
+ }
+}
+
+
+
+
+static void
+mousepad_window_action_prev_tab (GtkAction *action,
+ MousepadWindow *window)
+{
+ gint page_num;
+ gint n_pages;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+
+ /* get notebook info */
+ page_num = gtk_notebook_get_current_page (GTK_NOTEBOOK (window->notebook));
+ n_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (window->notebook));
+
+ /* switch to the previous tab or cycle to the last tab */
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (window->notebook), (page_num - 1) % n_pages);
+}
+
+
+
+static void
+mousepad_window_action_next_tab (GtkAction *action,
+ MousepadWindow *window)
+{
+ gint page_num;
+ gint n_pages;
+
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+
+ /* get notebook info */
+ page_num = gtk_notebook_get_current_page (GTK_NOTEBOOK (window->notebook));
+ n_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (window->notebook));
+
+ /* switch to the next tab or cycle to the first tab */
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (window->notebook), (page_num + 1) % n_pages);
+}
+
+
+
+static void
+mousepad_window_action_go_to_tab (GtkRadioAction *action,
+ GtkNotebook *notebook)
+{
+ gint page;
+
+ mousepad_return_if_fail (GTK_IS_NOTEBOOK (notebook));
+ mousepad_return_if_fail (GTK_IS_RADIO_ACTION (action));
+ mousepad_return_if_fail (GTK_IS_TOGGLE_ACTION (action));
+
+ /* leave when the menu is locked or this is not the active radio button */
+ if (lock_menu_updates == 0
+ && gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
+ {
+ /* get the page number from the action value */
+ page = gtk_radio_action_get_current_value (action);
+
+ /* set the page */
+ gtk_notebook_set_current_page (notebook, page);
+ }
+}
+
+
+
+static void
+mousepad_window_action_go_to_position (GtkAction *action,
+ MousepadWindow *window)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+ mousepad_return_if_fail (MOUSEPAD_IS_DOCUMENT (window->active));
+ mousepad_return_if_fail (GTK_IS_TEXT_BUFFER (window->active->buffer));
+
+ /* run jump dialog */
+ if (mousepad_dialogs_go_to (GTK_WINDOW (window), window->active->buffer))
+ {
+ /* put the cursor on screen */
+ mousepad_view_scroll_to_cursor (window->active->textview);
+ }
+}
+
+
+
+static void
+mousepad_window_action_contents (GtkAction *action,
+ MousepadWindow *window)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+
+ /* show help */
+ mousepad_dialogs_show_help (GTK_WINDOW (window), NULL, NULL);
+}
+
+
+
+static void
+mousepad_window_action_about (GtkAction *action,
+ MousepadWindow *window)
+{
+ mousepad_return_if_fail (MOUSEPAD_IS_WINDOW (window));
+
+ /* show about dialog */
+ mousepad_dialogs_show_about (GTK_WINDOW (window));
+}
diff --git a/mousepad/mousepad-window.h b/mousepad/mousepad-window.h
new file mode 100644
index 0000000..cf68bc4
--- /dev/null
+++ b/mousepad/mousepad-window.h
@@ -0,0 +1,59 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __MOUSEPAD_WINDOW_H__
+#define __MOUSEPAD_WINDOW_H__
+
+G_BEGIN_DECLS
+
+#include <mousepad/mousepad-document.h>
+
+#define MOUSEPAD_TYPE_WINDOW (mousepad_window_get_type ())
+#define MOUSEPAD_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MOUSEPAD_TYPE_WINDOW, MousepadWindow))
+#define MOUSEPAD_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MOUSEPAD_TYPE_WINDOW, MousepadWindowClass))
+#define MOUSEPAD_IS_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MOUSEPAD_TYPE_WINDOW))
+#define MOUSEPAD_IS_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MOUSEPADL_TYPE_WINDOW))
+#define MOUSEPAD_WINDOW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MOUSEPAD_TYPE_WINDOW, MousepadWindowClass))
+
+enum
+{
+ TARGET_TEXT_URI_LIST,
+ TARGET_GTK_NOTEBOOK_TAB
+};
+
+static const GtkTargetEntry drop_targets[] =
+{
+ { (gchar *) "text/uri-list", 0, TARGET_TEXT_URI_LIST },
+ { (gchar *) "GTK_NOTEBOOK_TAB", GTK_TARGET_SAME_APP, TARGET_GTK_NOTEBOOK_TAB }
+};
+
+typedef struct _MousepadWindowClass MousepadWindowClass;
+typedef struct _MousepadWindow MousepadWindow;
+
+GType mousepad_window_get_type (void) G_GNUC_CONST;
+
+GtkWidget *mousepad_window_new (void);
+
+void mousepad_window_add (MousepadWindow *window,
+ MousepadDocument *document);
+
+gboolean mousepad_window_open_files (MousepadWindow *window,
+ const gchar *working_directory,
+ gchar **filenames);
+
+G_END_DECLS
+
+#endif /* !__MOUSEPAD_WINDOW_H__ */
diff --git a/po/POTFILES.in b/po/POTFILES.in
new file mode 100644
index 0000000..a62d797
--- /dev/null
+++ b/po/POTFILES.in
@@ -0,0 +1,27 @@
+#
+# Sources
+#
+mousepad/main.c
+mousepad/mousepad-application.c
+mousepad/mousepad-dbus.c
+mousepad/mousepad-dialogs.c
+mousepad/mousepad-document.c
+mousepad/mousepad-encoding-dialog.c
+mousepad/mousepad-encoding.c
+mousepad/mousepad-file.c
+mousepad/mousepad-preferences.c
+mousepad/mousepad-print.c
+mousepad/mousepad-replace-dialog.c
+mousepad/mousepad-search-bar.c
+mousepad/mousepad-statusbar.c
+mousepad/mousepad-undo.c
+mousepad/mousepad-util.c
+mousepad/mousepad-view.c
+mousepad/mousepad-window.c
+
+
+
+#
+# Desktop Files
+#
+Mousepad.desktop.in.in
More information about the Xfce4-commits
mailing list