Freitag, 22. April 2011

Leere VS2010 Solution in Nautilus

Ich habe schon einmal geschrieben, wie man eine neue VS2008 Solution mit dem Explorer anlegen kann.
Das selbe ist natürlich auch für gnomes Nautilus möglch.
Einfach eine der Vorlagen (2008 oder 2010) ablegen im Template-Verzeichnis ablegen und mit "nautilus -q" Nautilus neu starten.

Das Template-Verzeichnis von Nautilus ist unter dem Namen XDG_TEMPLATES_DIR abgelegt in der Datei user-dirs.dirs des config-verzeichnisses. Dieses sollte in XDG_CONFIG_HOME stehen, standard ist ~./config.
Also im Lieblings-Editor (vim für mich...) die user-dirs.dir bearbeiten :
vim ${XDG_CONFIG_HOME:-~/.config}/user-dirs.dirs

und entweder herausfinden was der Wert der variablen ist, oder einen guten setzen. Meiner ist:
XDG_TEMPLATES_DIR="$HOME/.XdgTemplates"

Also Kurzfassung:

  1. XDG_TEMPLATES_DIR finden
  2. Vorlage (2008 oder 2010) im XDG_TEMPLATES_DIR ablegen 
  3. Nautilus neustarten (nautilus -q)

vim probleme mit delete unter rxvt?

In meinem letzten post hatte ich dargestellt, wie man einen netten terminal-emulator (rxvt) unter cygwin zum laufen bringt. Soweit so gut.
Jetzt machte aber plötzlich mein vim probleme: Del funktionierte manchmal nicht und die Pfeiltasten verhielten sich auch komisch..
Die Antwort war wie immer recht einfach: Ich hatte die .vimrc vergessen.
cp /usr/share/vim/vim73/vimrc_example.vim ~/.vimrc
Behebt das Problem. Super-Einfache lösung.
(Nur vorsicht, wenn man schon eine .vimrc hat...)

EDIT:
Natürlich ist es nicht „die magische .vimrc“, die das Problem behebt, sondern die folgende Zeile innerhalb dieser:
set backspace=indent,eol,start

Besserer Terminal-Emulator für cygwin

Seit kurzem befasse ich mich (mal wieder) mit cygwin. Eine gute Idee habe ich heute im Netz gefunden: Den bestehenden emulator gegen rxvt ersetzen.
Dafür muss natürlich rxvt in der cygwin-umgebung installiert sein.
Folgende bat-Datei sollte dann zum starten erstellt werden:
@echo off
C:
chdir C:\cygwin\bin
start rxvt -sr -sl 10000 -fg white -bg black -fn "Lucida Console-10" -tn cygwin -e /bin/bash --login -i

Super.

Mittwoch, 23. März 2011

Word-Dokumente zusammenfassen

Mein aktuelles Problem: Mehrere Word-Dokumente sollten in einem neuem Dokument zusammengefasst werden.

Zwei mögliche Lösungen bieten sich an:
1. Neues Word-Dokument erstellen, Einfügen, Objekt, „Text aus Datei”
2. Ein Makro erstellen, dass alle Dateien in einem bestimmten Ordner öffnet und in eine neue Datei einfügt (ohne Beachtung einer eventuell gewünschten Reihenfolge).

Da mir Variante 2 besser gefällt (*grins*) folgt der verwendete code:

Sub ConcatenateAllWordFiles()
 Dim mypath
 mypath = BrowseFolder("Verzeichnis Wählen")
 
 Set fso = CreateObject("Scripting.FileSystemObject")
 Set Folder = fso.GetFolder(mypath)
 Set allFiles = Folder.Files
 Set newDoc = Documents.Add

 For Each file In allFiles
    If Right(file, 5) = ".docx" Then

        Documents.Open FileName:=file.Path
        current = ActiveDocument.Name
        Selection.WholeStory
        Selection.Copy
        Documents(current).Close
        newDoc.Activate
        Selection.Paste
        Selection.EndKey Unit:=wdLine
    End If
 Next
End Sub

Function BrowseFolder(Title As String, _
        Optional InitialFolder As String = vbNullString, _
        Optional InitialView As Office.MsoFileDialogView = _
            msoFileDialogViewList) As String
    Dim V As Variant
    Dim InitFolder As String
    With Application.FileDialog(msoFileDialogFolderPicker)
        .Title = Title
        .InitialView = InitialView
        If Len(InitialFolder) > 0 Then
            If Dir(InitialFolder, vbDirectory) <> vbNullString Then
                InitFolder = InitialFolder
                If Right(InitFolder, 1) <> "\" Then
                    InitFolder = InitFolder & "\"
                End If
                .InitialFileName = InitFolder
            End If
        End If
        .Show
        On Error Resume Next
        Err.Clear
        V = .SelectedItems(1)
        If Err.Number <> 0 Then
            V = vbNullString
        End If
    End With
    BrowseFolder = CStr(V)
End Function

Das ist natürlich nicht alles meine alleinige Idee, die Grobe Vorlage war dies.
Wie BrowseFolder-Funktion habe ich komplett „gefunden”...

Samstag, 24. Juli 2010

Nautilus-Erweiterung mit Python

Heute gab es bei mir ja schon eine Explorer-Erweiterung mit Python. Das gleiche benötige ich auch am heimischen Rechner - nur unter Nautilus:
#!/usr/bin/python
# -*- coding: utf-8 -*-

import urllib
import easygui
import logging
import nautilus
import os

CCDFOLDERS =  [
 'source',
 'bin',
 'build',
 'lib',
 'resource',
 ]

class CcdDirStructureExtension(nautilus.MenuProvider):

 def __init__(self):
  pass
 
 
 def _alertError(self, message):
  easygui.msgbox(message, title='Alert', ok_button='Mist!')
 
 
 def _create_ccd_structure(self, foldername):
  ok = easygui.ynbox(msg=u'wirklich eine CCD-Struktur ersellen in\n' + foldername,
   title=u'Erstellen bestägen', choices=('Ja, bitte', 'Nein, danke'))
  if ok == 0:
   return

  if  not os.path.isdir(foldername):
   self._alertError(foldername + u'\nist kein Verzeichnis. Abgebrochen.')
   return

  try:
   for ccd in CCDFOLDERS:
    dir = os.path.join(foldername, ccd)
    os.mkdir(dir)
  except Exception, e:
   self._alertError(e)
   raise
 
 
 def menu_activate(self, menu, file):
  """Called when the user selects the menu."""
  filename = urllib.unquote(file.get_uri()[7:])
  self._create_ccd_structure(filename)


 def get_file_items(self, window, files):
  """Called when the user selects a file in Nautilus."""  
  if len(files) != 1:
   return
   
  file = files[0]
  if not file.is_directory() or file.get_uri_scheme() != 'file':
   return

  return self._get_MenuItemForFile(file)
  

 def get_background_items(self, window, file):
  """Called when the user clicks the background of a Nautilus-Window"""
  if not file.is_directory() or file.get_uri_scheme() != 'file':
   return
  return self._get_MenuItemForFile(file)


 def _get_MenuItemForFile(self, file):
  item = nautilus.MenuItem("NautilusPython::ccd-dir::create",
   "CCD-Struktur", 
   "erstellt eine CCD-Verzeichnisstruktur in %s" % file.get_name())
  item.connect("activate", self.menu_activate, file)
  return item,


if __name__ == '__main__':
 print 'This is a Nautilus extenion. Copy it to ${HOME}/.nautilus/python-extensions/ and restart Nautilus. Have fun.'
Der Code gehört bei Installierten Nautilus-Python-Bindings nach ${HOME}/.nautilus/python‑extensions/ und sollte ausführbar sein (chmod 755).
Die Dialoge werden von EasyGui erstellt, irgendwie gab es Probleme gtk-Dialoge zu erzeugen...

Explorer-Erweiterung (shell extension) mit Python

Die Problemstellung: Eine Verzeichnisstruktur soll automatisch erstellt werden, und zwar über einen Eintrag im Context-Menü des Explorers.
Die ganze Vorgeschichte ist hier.
Nun die Lösung in Python. Das ganze ist recht übersichtlich:

# -*- coding: utf-8 -*-

#   Erstellt einen Eintrag im Context-Menü des Explorers: CCD-Verzeichnis erstellen
#   bei Click wird im gewählten Verzeichnis eine CCD-Verzeichnisstrukur
#   (bin, build, lib, source, resources) erstellt.

import os.path
import pythoncom
from win32com.shell import shell, shellcon
import win32gui
import win32con

IContextMenu_Methods = ["QueryContextMenu", "InvokeCommand", "GetCommandString"]
IShellExtInit_Methods = ["Initialize"]

#   HKCR Key   Affected object types
#      *    All files
#AllFileSystemObjects  All regular files and file folders
#     Folder   All folders, virtual and filesystem
#     Directory   File folders
#Directory\Background  Directory-Background (Folder is open, one clicks on the white background...)
#      Drive   Root folders of all system drives
#    Network   Entire network
#   NetShare   All network shares

TYPES = [
 'Directory\\Background',
 'Directory',
 ]
SUBKEY = 'CCD-Verzeichnis'

CCDFOLDERS = [
 'source',
 'bin',
 'build',
 'lib',
 'resource',
 ]

def alertError(hwnd, exc):
 win32gui.MessageBox(hwnd, str(exc), str(exc.__class__), win32con.MB_OK)


class ShellExtension:
 _reg_progid_ = "CCD.Verzeichnisersteller.ShellExtension.ContextMenu"
 _reg_desc_ = "CCD-Verzeichnis Shell Extension (context menu)"
 _reg_clsid_ = "{5C664DC4-5ADA-4385-9DEB-EDB51320A668}"

 _com_interfaces_ = [shell.IID_IShellExtInit, shell.IID_IContextMenu]
 _public_methods_ = IContextMenu_Methods + IShellExtInit_Methods

 def Initialize(self, folder, dataobj, hkey):
  self.dataobj = dataobj

 def QueryContextMenu(self, hMenu, indexMenu, idCmdFirst, idCmdLast, uFlags):
  try:
   msg = 'CCD-Verzeichnis ersellen'

   idCmd = idCmdFirst
   items = []
   if (uFlags &amp; 0x000F) == shellcon.CMF_NORMAL:
    items.append(msg)
   elif uFlags &amp; shellcon.CMF_VERBSONLY:
    items.append(msg)
   elif uFlags &amp; shellcon.CMF_EXPLORE:
    items.append(msg)
   else:
    pass
    
   win32gui.InsertMenu(hMenu, indexMenu,
        win32con.MF_SEPARATOR|win32con.MF_BYPOSITION,
        0, None)
   indexMenu += 1
   for item in items:
    win32gui.InsertMenu(hMenu, indexMenu,
         win32con.MF_STRING|win32con.MF_BYPOSITION,
         idCmd, item)
    indexMenu += 1
    idCmd += 1

   win32gui.InsertMenu(hMenu, indexMenu,
        win32con.MF_SEPARATOR|win32con.MF_BYPOSITION,
        0, None)
   indexMenu += 1
   return idCmd-idCmdFirst

  except Exception, e:
   alertError(None, e)
   raise


 def InvokeCommand(self, ci):
  mask, hwnd, verb, params, dir, nShow, hotkey, hicon = ci
  
  try:
   if self.dataobj is None:
    #background-click
    fname = dir
   else:
    #get Files from dragObject
    files = self.getDragFiles()
    if not files:
     return
    fname = files[0]

   self.CreateCCDFolderStructure(hwnd, fname)
  except Exception, e:
   alertError(hwnd, e)
   raise
  return


 def GetCommandString(self, cmd, typ):
  return "Erstellt eine CCD-Verzeichnisstruktur"


 def getDragFiles(self):
  # Format the DataObject using a formatec, then get DragQueryFile from it...
  format_etc = win32con.CF_HDROP, None, 1, -1, pythoncom.TYMED_HGLOBAL
  sm = self.dataobj.GetData(format_etc)
  num_files = shell.DragQueryFile(sm.data_handle, -1)
  files = [shell.DragQueryFile(sm.data_handle, i) for i in range(num_files)]
  return files
  
  
 def CreateCCDFolderStructure(self, hwnd, folder):
  ok = win32gui.MessageBox(hwnd, u'wirklich eine CCD-Struktur ersellen in\n' + folder,
   u'Erstellen bestägen', 
   win32con.MB_YESNO|win32con.MB_ICONQUESTION|win32con.MB_TASKMODAL|win32con.MB_SETFOREGROUND)
  if ok != win32con.IDYES:
   return
  
  if not os.path.isdir(folder):
   alertError(hwnd, folder + u'\nist kein Verzeichnis. Abgebrochen.')
  
  try:
   for ccd in CCDFOLDERS:
    dir = os.path.join(folder, ccd)
    os.mkdir(dir)
  except Exception, e:
   alertError(hwnd, e)
   raise
  return 

def DllRegisterServer():
 import _winreg
 for typ in TYPES:
  key = _winreg.CreateKey(_winreg.HKEY_CLASSES_ROOT, "%s\\shellex" % typ)
  subkey = _winreg.CreateKey(key, "ContextMenuHandlers")
  subkey2 = _winreg.CreateKey(subkey, SUBKEY)
  _winreg.SetValueEx(subkey2, None, 0, _winreg.REG_SZ, ShellExtension._reg_clsid_)
 print ShellExtension._reg_desc_, "registration complete."


def DllUnregisterServer():
 import _winreg
 for typ in TYPES:
  try:
   key = _winreg.DeleteKey(_winreg.HKEY_CLASSES_ROOT, "%s\\shellex\\ContextMenuHandlers\\%s" % (typ, SUBKEY))
  except WindowsError, details:
   import errno
   if details.errno != errno.ENOENT:
    raise
 print ShellExtension._reg_desc_, "unregistration complete."

 
def main(argv):
 from win32com.server import register
 register.UseCommandLine(ShellExtension,
       finalize_register = DllRegisterServer,
       finalize_unregister = DllUnregisterServer)

if __name__=='__main__':
 import sys
 main(sys.argv)

Für die Verwendung muss Python installiert sein (Empfehlung: 2.6) und passend zu der Python-Version PyWin32 (Empfehlung: 214.win32-py2.6)

Die Python-Datei in ein „kluges“ Verzeichnis legen (z.B. %ProgramFiles%\ccd‑verzeichnis\) und in der registry registrieren per doppelklick, oder über die Eingabeaufforderung mit ccd-verzeichnis.py --register. Wenn das ganze wieder entfernt werden soll, so geht dies nur über die Eingabeaufforderung mit ccd-verzeichnis.py --unregister.

Viel Spass.

Edits:
- Das ganze gibt's natürlich auch als Nautilus-Erweiterung...
- Vorlage für den Code war eine Beispiel-Anwendung des PyWin32-Projektes

CCD-Projekt Verzeichnisstruktur automatisiert erstellen

Im „Clean Code Developer Camp“ bekam ich die Empfehlung, dass die Verzeichnisstruktur in einem Projekt (Entwicklungsverzeichnisse, keine Abrechnungen, Verträge oder so...) einheitlich sein sollte. Die Empfehlung war unter dem Toplevel die Folgenden Unterverzeichnisse:
  • bin - Alles, was erstellt wird (start.exe, ergänzung.dll, …) landet hier.
  • build - Hier findet sich das Buildskript um das Projekt komplett zu bauen
  • lib - 3rd-Party-Tools finden sich hier.
  • resources - Hier findet sich alles, was sonst noch benötigt wird um das fertige Projekt laufen zu lassen
  • source - Die Sourcen...
Da ich das Problem habe mir das nicht immer merken zu können und außerdem eine manuelle Anlage dieser Struktur immer Zeit kostet habe ich mir gedacht ich schreibe einfach mal eine Erweiterung für den Explorer, mit der Funktion diese Struktur in einem Verzeichnis zu erstellen.

Für den durchschnittlichen nicht-c-Programmierer ist das aber alles andere als einfach habe ich herausfinden müssen.  
Meine Erste Idee war c#, mit P/Invoke... Das hat bisher nicht funktioniert wenn ich das zum Laufen bekomme, werde ich darüber berichten.
Der aktuelle Ansatz ist Python, mit PyWin32. Das scheint recht simpel.