diff --git a/dub.json b/dub.json index c23bd2e..63f89d4 100644 --- a/dub.json +++ b/dub.json @@ -7,7 +7,7 @@ "buildRequirements": ["allowWarnings"], "versions": ["StdLoggerDisableTrace"], "dependencies": { - "gtk-d": ">=3.1.4", + "gtk-d": ">=3.2.2", "sdlang-d": "~>0.9.3" } } diff --git a/dub.selections.json b/dub.selections.json index bbd3fd8..55e7cd4 100644 --- a/dub.selections.json +++ b/dub.selections.json @@ -2,7 +2,7 @@ "fileVersion": 1, "versions": { "libinputvisitor": "1.2.0", - "gtk-d": "3.2.0", + "gtk-d": "~master", "sdlang-d": "0.9.3" } } diff --git a/dub.userprefs b/dub.userprefs index 315df1b..b4d912e 100644 --- a/dub.userprefs +++ b/dub.userprefs @@ -1,6 +1,12 @@  - + + + + + + + diff --git a/extract-strings.sh b/extract-strings.sh new file mode 100755 index 0000000..c8aa1a5 --- /dev/null +++ b/extract-strings.sh @@ -0,0 +1,34 @@ +#!/bin/sh +DOMAIN=vgrep +BASEDIR=$(dirname $0) +OUTPUT_FILE=${BASEDIR}/po/${DOMAIN}.pot + +echo "Extracting translatable strings... " + +# Extract the strings from D source code. Since xgettext does not support D +# as a language we use Vala, which works reasonable well. +find ${BASEDIR}/source -name '*.d' | xgettext \ + --join-existing \ + --output $OUTPUT_FILE \ + --files-from=- \ + --directory=$BASEDIR \ + --language=Vala \ + --from-code=utf-8 + +xgettext \ + --join-existing \ + --output $OUTPUT_FILE \ + --default-domain=$DOMAIN \ + --package-name=$DOMAIN \ + --directory=$BASEDIR \ + --foreign-user \ + --language=Desktop \ + ${BASEDIR}/pkg/desktop/com.gexperts.VisualGrep.desktop.in + +# Merge the messages with existing po files +echo "Merging with existing translations... " +for file in ${BASEDIR}/po/*.po +do + echo -n $file + msgmerge --update $file $OUTPUT_FILE +done diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..297df85 --- /dev/null +++ b/install.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env sh + +if [ -z "$1" ]; then + export PREFIX=/usr + # Make sure only root can run our script + if [ "$(id -u)" != "0" ]; then + echo "This script must be run as root" 1>&2 + exit 1 + fi +else + export PREFIX=$1 +fi + +echo "Installing to prefix ${PREFIX}" + +# Compile po files +echo "Copying and installing localization files" +for f in po/*.po; do + echo "Processing $f" + LOCALE=$(basename "$f" .po) + mkdir -p ${PREFIX}/share/locale/${LOCALE}/LC_MESSAGES + msgfmt $f -o ${PREFIX}/share/locale/${LOCALE}/LC_MESSAGES/vgrep.mo +done + +# Generate desktop file +msgfmt --desktop --template=pkg/desktop/com.gexperts.VisualGrep.desktop.in -d po -o pkg/desktop/com.gexperts.VisualGrep.desktop + +# Copy executable and desktop file +mkdir -p ${PREFIX}/bin +cp vgrep ${PREFIX}/bin/vgrep +mkdir -p ${PREFIX}/share/applications +cp pkg/desktop/com.gexperts.VisualGrep.desktop ${PREFIX}/share/applications diff --git a/pkg/createReleaseArchive.sh b/pkg/createReleaseArchive.sh new file mode 100755 index 0000000..9380dfe --- /dev/null +++ b/pkg/createReleaseArchive.sh @@ -0,0 +1,18 @@ +export VGREP_ARCHIVE_PATH="/tmp/vgrep/archive"; + +rm -rf ${VGREP_ARCHIVE_PATH} + +CURRENT_DIR=$(pwd) + +echo "Building application..." +cd .. +dub build --build=release + +./install.sh ${VGREP_ARCHIVE_PATH}/usr + +echo "Creating archive" +cd ${VGREP_ARCHIVE_PATH} +zip -r vgrep.zip * + +cp vgrep.zip ${CURRENT_DIR}/vgrep.zip +cd ${CURRENT_DIR} diff --git a/pkg/desktop/com.gexperts.VisualGrep.desktop b/pkg/desktop/com.gexperts.VisualGrep.desktop new file mode 100644 index 0000000..5041cbe --- /dev/null +++ b/pkg/desktop/com.gexperts.VisualGrep.desktop @@ -0,0 +1,10 @@ +[Desktop Entry] +Name=Visual Grep +Comment=A simple GTK3 GUI for grep +Exec=vgrep +Terminal=false +Type=Application +StartupNotify=true +Categories=Utilities +Icon=search +Name[en_US]=com.gexperts.VisualGrep.desktop.in diff --git a/pkg/desktop/com.gexperts.VisualGrep.desktop.in b/pkg/desktop/com.gexperts.VisualGrep.desktop.in new file mode 100644 index 0000000..5041cbe --- /dev/null +++ b/pkg/desktop/com.gexperts.VisualGrep.desktop.in @@ -0,0 +1,10 @@ +[Desktop Entry] +Name=Visual Grep +Comment=A simple GTK3 GUI for grep +Exec=vgrep +Terminal=false +Type=Application +StartupNotify=true +Categories=Utilities +Icon=search +Name[en_US]=com.gexperts.VisualGrep.desktop.in diff --git a/source/app.d b/source/app.d index eab04ed..2944968 100644 --- a/source/app.d +++ b/source/app.d @@ -10,6 +10,8 @@ import gtk.Main; import gtk.Version; import gtk.MessageDialog; +import i18n.l10n; + import vg.application; import vg.constants; @@ -17,7 +19,10 @@ int main(string[] args) { //Version checking cribbed from grestful, thanks! string error = Version.checkVersion(GTK_VERSION_MAJOR, GTK_VERSION_MINOR, GTK_VERSION_PATCH); - + + //textdomain + textdomain("vgrep"); + if (error !is null) { Main.init(args); @@ -26,7 +31,7 @@ int main(string[] args) { DialogFlags.MODAL, MessageType.ERROR, ButtonsType.OK, - "Your GTK version is too old, you need at least GTK " ~ + _("Your GTK version is too old, you need at least GTK ") ~ to!string(GTK_VERSION_MAJOR) ~ '.' ~ to!string(GTK_VERSION_MINOR) ~ '.' ~ to!string(GTK_VERSION_PATCH) ~ '!', diff --git a/source/util/file/search.d b/source/util/file/search.d index e3941fd..e549d48 100644 --- a/source/util/file/search.d +++ b/source/util/file/search.d @@ -136,7 +136,7 @@ void search(Criteria criteria, MarkupTags markup, Tid tid) { ulong totalMatchCount = 0; foreach (DirEntry entry; dirEntries(criteria.path, wildcard, mode, criteria.followSymbolic).filter!(a => a.isFile)) { - info("Searching file ", entry.name); + trace("Searching file ", entry.name); char[] buf; string path = dirName(entry.name); if (!path.equal(currentPath)) { @@ -170,7 +170,7 @@ void search(Criteria criteria, MarkupTags markup, Tid tid) { } if (matches.length>0) { - info("Found %d matches in %s", matchCount, entry.name); + trace(format("Found %d matches in %s", matchCount, entry.name)); shared Result result = {entry.name, matches, matchCount}; totalMatchCount = totalMatchCount + matchCount; tid.send(Status.PROGRESS_RESULT, criteria.id, result); diff --git a/source/vg/application.d b/source/vg/application.d index 54f86a3..0182b67 100644 --- a/source/vg/application.d +++ b/source/vg/application.d @@ -18,6 +18,8 @@ import gtk.Dialog; import gtk.util; +import i18n.l10n; + import vg.appwindow; import vg.constants; import vg.configuration; @@ -123,8 +125,8 @@ private: with (menu = new Menu()) { - append("About", "app.about"); - append("Quit", "app.quit"); + append(_("About"), "app.about"); + append(_("Quit"), "app.quit"); } this.setAppMenu(menu); diff --git a/source/vg/appwindow.d b/source/vg/appwindow.d index cd87f3d..1cb9293 100644 --- a/source/vg/appwindow.d +++ b/source/vg/appwindow.d @@ -43,6 +43,8 @@ import gtk.util; import gtk.threads; import util.file.search; +import i18n.l10n; + import vg.configuration; import vg.finddialog; import vg.search; @@ -69,14 +71,14 @@ private: //Create header bar hb = new HeaderBar(); hb.setShowCloseButton(true); - hb.setTitle("Visual Grep"); + hb.setTitle(_("Visual Grep")); this.setTitlebar(hb); //Create Find button btnFind = new Button(StockID.FIND); btnFind.setAlwaysShowImage(true); btnFind.addOnClicked(&showFindDialog); - btnFind.setTooltipText("Initiate new search"); + btnFind.setTooltipText(_("Initiate new search")); hb.packStart(btnFind); @@ -84,7 +86,7 @@ private: Button btnNew = new Button("tab-new-symbolic", IconSize.BUTTON); btnNew.setAlwaysShowImage(true); btnNew.addOnClicked(&createNewTab); - btnNew.setTooltipText("Create a new tab"); + btnNew.setTooltipText(_("Create a new tab")); hb.packStart(btnNew); @@ -110,7 +112,7 @@ private: string id = randomUUID.toString(); ResultPage page = new ResultPage(manager, id); pages[id] = page; - TabLabel label = new TabLabel("Search", page); + TabLabel label = new TabLabel(_("Search"), page); label.addOnCloseClicked(&closePage); nb.appendPage(page, label); nb.showAll(); @@ -236,7 +238,7 @@ public: this(Application application) { super(application); cbThreadIdle = &this.checkPendingSearches; - setTitle("Visual Grep"); + setTitle(_("Visual Grep")); setIconName("search"); manager = new SearchManager(); @@ -281,10 +283,10 @@ private: ScrolledWindow scrollResults = new ScrolledWindow(); scrollResults.add(tvResults); - TreeViewColumn column = new TreeViewColumn("File", new CellRendererText(), "text", 0); + TreeViewColumn column = new TreeViewColumn(_("File"), new CellRendererText(), "text", 0); column.setExpand(true); tvResults.appendColumn(column); - tvResults.appendColumn(new TreeViewColumn("Matches", new CellRendererText(), "text", 1)); + tvResults.appendColumn(new TreeViewColumn(_("Matches"), new CellRendererText(), "text", 1)); lsResults = new ListStore([GType.STRING, GType.LONG]); tvResults.setModel(lsResults); @@ -303,8 +305,8 @@ private: ScrolledWindow scrollMatches = new ScrolledWindow(); scrollMatches.add(tvMatches); - tvMatches.appendColumn(new TreeViewColumn("Line", new CellRendererText(), "text", 0)); - column = new TreeViewColumn("Match", new CellRendererText(), "markup", 1); + tvMatches.appendColumn(new TreeViewColumn(_("Line"), new CellRendererText(), "text", 0)); + column = new TreeViewColumn(_("Match"), new CellRendererText(), "markup", 1); column.setExpand(true); tvMatches.appendColumn(column); @@ -323,7 +325,7 @@ private: btnAbort.setRelief(ReliefStyle.NONE); btnAbort.setFocusOnClick(false); btnAbort.setSensitive(false); - btnAbort.setTooltipText("Abort the current search"); + btnAbort.setTooltipText(_("Abort the current search")); btnAbort.addOnClicked(&abortSearch); b.add(btnAbort); @@ -405,12 +407,12 @@ public: void searchStart(Criteria value) { this._criteria = value; clear(); - sbStatus.push(1, "Starting..."); + sbStatus.push(1, _("Starting...")); btnAbort.setSensitive(true); } void searchProgress(string currentPath) { - sbStatus.push(1, format("Search %s", currentPath)); + sbStatus.push(1, format(_("Search %s"), currentPath)); } void searchResult(Result result) { @@ -423,7 +425,7 @@ public: void searchCompleted(bool aborted, ulong total) { sbStatus.removeAll(1); - sbStatus.push(0, format("%s, total matches found %d", aborted?"Aborted":"Completed", total)); + sbStatus.push(0, format(_("%s, total matches found %d"), aborted?"Aborted":"Completed", total)); btnAbort.setSensitive(false); } } \ No newline at end of file diff --git a/source/vg/constants.d b/source/vg/constants.d index 57da3d2..c85ca50 100644 --- a/source/vg/constants.d +++ b/source/vg/constants.d @@ -11,7 +11,7 @@ immutable uint GTK_VERSION_PATCH = 0; immutable string APPLICATION_ID = "com.gexperts.VisualGrep"; immutable string APPLICATION_NAME = "Visual Grep"; -immutable string APPLICATION_VERSION = "0.1"; +immutable string APPLICATION_VERSION = "0.3.0"; immutable string APPLICATION_AUTHOR = "Gerald Nunn"; immutable string APPLICATION_COPYRIGHT = "Copyright \xc2\xa9 2015 " ~ APPLICATION_AUTHOR; immutable string APPLICATION_COMMENTS = "A GTK+ gui for the grep utility"; diff --git a/source/vg/finddialog.d b/source/vg/finddialog.d index f8a6083..70f839b 100644 --- a/source/vg/finddialog.d +++ b/source/vg/finddialog.d @@ -5,6 +5,7 @@ module vg.finddialog; import std.container; +import std.format; import gtk.Box; import gtk.Button; @@ -20,6 +21,8 @@ import gtk.Window; import util.file.search; +import i18n.l10n; + import vg.configuration; class FindDialog: Dialog { @@ -38,7 +41,7 @@ private: void createUI() { setDefaultSize(800,300); - //Create Grid Layour + //Create Grid Layout Grid grid = new Grid(); grid.setColumnSpacing(12); @@ -50,20 +53,20 @@ private: int row = 0; //Search Label Title Text - Label label = new Label("Search Name"); + Label label = new Label(format("%s", _("Search Name"))); label.setUseMarkup(true); label.setHalign(Align.START); grid.attach(label,0,row,2,1); row++; //Search Label - grid.attach(createLabel("Name"),0,row,1,1); + grid.attach(createLabel(_("Name")),0,row,1,1); eSearchName = new Entry(); grid.attach(eSearchName, 1,row,1,1); row++; //Search Text Title Label - label = new Label("Search Text"); + label = new Label(format("%s", _("Search Text"))); label.setUseMarkup(true); label.setHalign(Align.START); label.setMarginTop(18); @@ -71,7 +74,7 @@ private: row++; //Search Pattern - grid.attach(createLabel("Pattern"),0,row,1,1); + grid.attach(createLabel(_("Pattern")),0,row,1,1); cbPattern = new ComboBoxText(true); cbPattern.setHexpand(true); cbPattern.addOnChanged(&changeListener); @@ -81,12 +84,12 @@ private: //Case Insensitive swCaseInsensitive = new Switch(); swCaseInsensitive.setHalign(GtkAlign.START); - grid.attach(createLabel("Case Insenstive"),0,row,1,1); + grid.attach(createLabel(_("Case Insenstive")),0,row,1,1); grid.attach(swCaseInsensitive,1,row,1,1); row++; //Search Path Ttitle Label - label = new Label("Search Path"); + label = new Label(format("%s", _("Search Path"))); label.setUseMarkup(true); label.setHalign(Align.START); label.setMarginTop(18); @@ -94,7 +97,7 @@ private: row++; //Search Path - grid.attach(createLabel("Path"),0,row,1,1); + grid.attach(createLabel(_("Path")),0,row,1,1); cbPath = new ComboBoxText(true); cbPath.setHexpand(true); cbPath.addOnChanged(&changeListener); @@ -108,7 +111,7 @@ private: row++; //Mask - grid.attach(createLabel("File Mask"),0,row,1,1); + grid.attach(createLabel(_("File Mask")),0,row,1,1); cbMask = new ComboBoxText(true); cbMask.setHexpand(true); cbMask.addOnChanged(&changeListener); @@ -118,14 +121,14 @@ private: //Create Search Subdirectories swSearchSubdirectories = new Switch(); swSearchSubdirectories.setHalign(GtkAlign.START); - grid.attach(createLabel("Search Subdirectories"),0,row,1,1); + grid.attach(createLabel(_("Search Subdirectories")),0,row,1,1); grid.attach(swSearchSubdirectories,1,row,1,1); row++; //Create Follow Symlinks Row swFollowSymbolic = new Switch(); swFollowSymbolic.setHalign(GtkAlign.START); - grid.attach(createLabel("Follow Symlinks"),0,row,1,1); + grid.attach(createLabel(_("Follow Symlinks")),0,row,1,1); grid.attach(swFollowSymbolic,1,row,1,1); row++; @@ -149,7 +152,7 @@ private: } void selectPath(Button button) { - FileChooserDialog dialog = new FileChooserDialog("Select Path", this, FileChooserAction.SELECT_FOLDER, ["Open", "Cancel"], [ResponseType.OK, ResponseType.CANCEL]); + FileChooserDialog dialog = new FileChooserDialog(_("Select Path"), this, FileChooserAction.SELECT_FOLDER, [_("Open"), _("Cancel")], [ResponseType.OK, ResponseType.CANCEL]); scope(exit) dialog.destroy(); dialog.setDefaultResponse(ResponseType.OK); @@ -168,7 +171,7 @@ private: public: this(Window parent, string id) { - super("Find",parent,GtkDialogFlags.MODAL+GtkDialogFlags.USE_HEADER_BAR,[StockID.CANCEL,StockID.OK],[ResponseType.CANCEL,ResponseType.OK]); + super(_("Find"),parent,GtkDialogFlags.MODAL+GtkDialogFlags.USE_HEADER_BAR,[StockID.CANCEL,StockID.OK],[ResponseType.CANCEL,ResponseType.OK]); setDefaultResponse(ResponseType.OK); this.id = id; createUI();