■
16日追記。近日中に完全版にするので、それまで待ったほうがいいかもー。
CraftLaunchEXでクリップボードの履歴を取れたら、時計を表示するよりは便利そうだと思ってclmode_clockの代わりに作ってみた。例によってwin32関連のモジュールやPythonの実行環境などが正しく設定されていないと使えない。動作確認はWindowsXPで行ったため、Windows98やMEなどのDOS系OSで正常動作するかどうかはわからない(ユニコードの関連であまりうまく動かないような気もする)。
モードを通常起動すると、現在のクリップボードの内容を表示する。タイマーでクリップボードの内容を監視し、変更があったら取得する。デフォルトの設定の場合、履歴は16個保存される。基本的にclockの代替モードを想定しているので、CraftLaunchEXがアクティブになった場合はランチャーモードに復帰する。
キーハンドラにより、Shortcut_ClipMode()が呼び出された場合、クリップボード履歴のリストを表示し、アクティブになる。方向キーで履歴を選択し、Enterで決定。決定されるとクリップボードの内容が変更され、通常モードに移行する(CraftLaunchEXはフォーカスを失う)。デフォルトの設定ではCtrl+Qでランチャーモードに復帰。
相変わらずエンコード周りの処理やらエラートラップやらなにやらは適当。
# -*- coding: utf_8 -*- # 表示に使用する最長の文字列長 max_string_width = 40 # 履歴の最大サイズ max_dict_size = 16 # クリップボード監視間隔 timer_interval = 100 # 履歴の保存先(変更はあまり推奨しません) file_cliplist = 'clip.dat' from clapi import * from clconst import * from clmode import BaseMode from win32clipboard import * import win32api CLIPMODE_NORMAL = 0 CLIPMODE_ACTIVE = 1 class ClipList : def __init__(self): self._cursor = 0 self._cliptext = self._cliptitle = def __del__(self): pass def __len__(self): return len(self._cliptitle) def add(self, str): if str == u'' or str == '': return # make title string title = u'' import string pos = string.find(str, '\n') if pos > -1: title = string.lstrip(str[:pos - 1]) else : title = string.lstrip(str) if len(title) > max_string_width: title = title[:max_string_width] if str not in self._cliptext : self._cliptext.insert(0, str) self._cliptitle.insert(0, title) if len(self) > max_dict_size: self.pop() return title def clear(self): self._cliptext.clear self._cliptitle.clear def has_value(self, str): return str in self._cliptext def pop(self, index = -1): rv = self._cliptext.pop(index) self._cliptitle.pop(index) return rv def SetCursor(self, index = 0): self._cursor = index return self._cliptext[index] def GetCursor(self): return self._cursor def GetText(self): return self._cliptext def GetTitle(self): return self._cliptitle # Load clip_list clip_list = ClipList() import os.path import cPickle if os.path.exists( file_cliplist ): f = open(file_cliplist, 'rb+') clip_list = cPickle.load(f) f.close() print '***** Clip service started *****' class ClipMode( BaseMode ): def __init__(self, mode = CLIPMODE_NORMAL): BaseMode.__init__(self) self.clipmode_active = CLIPMODE_NORMAL self.SetClipmode(mode) self.SetKeyDownHandler( ord('Q'), MODKEY_CTRL, self.PopMode ) def __del__(self): BaseMode.__del__(self) def OnPush(self): BaseMode.OnPush(self) def OnPop(self): BaseMode.OnPop(self) def OnGetControl(self): SetOption( OPTION_ABBREV_COMMAND, False ) SetOption( OPTION_ABBREV_HISTORY, False ) SetOption( OPTION_ABBREV_FILELIST, False ) BaseMode.OnGetControl(self) def OnLoseControl(self): SetOption( OPTION_ABBREV_COMMAND, True ) SetOption( OPTION_ABBREV_HISTORY, True ) SetOption( OPTION_ABBREV_FILELIST, True) RemoveTimerHandler( self.OnTimer ) BaseMode.OnLoseControl(self) def OnTimer( self ): OpenClipboard() try: str = unicode(GetClipboardData(CF_TEXT), 'mbcs') except: str = u'' CloseClipboard() v = GetValue() title = clip_list.add(str) if v != title : SetValue( title ) # save cliptext cliptext = str # save clip_list import cPickle f = open(file_cliplist, 'wb') cPickle.dump(clip_list, f) f.close() def OnActivate( self, event ): BaseMode.OnActivate( self, event ) if event.active: active_bg_color = GetOption( OPTION_ACTIVE_BG_COLOR ) SetBgColor( *active_bg_color ) if self.clipmode_active == CLIPMODE_NORMAL: SetValue('') self.PopMode() else: inactive_bg_color = GetOption( OPTION_INACTIVE_BG_COLOR ) SetBgColor( *inactive_bg_color ) if self.clipmode_active == CLIPMODE_ACTIVE: self.SetClipmode( CLIPMODE_NORMAL ) PopList() def OnKeyDown( self, event ): if event.vk == VK_ESCAPE: self.SetClipmode( CLIPMODE_NORMAL ) import clcore clcore.RaiseNextWindow() event.Skip() #if event.vk == VK_BACK: # str = GetValue() # SetValue(str[0:len(str)-1]) # event.Skip() #if event.vk == VK_DELETE: # str = GetValue() # SetValue(str[1:len(str)]) # event.Skip() BaseMode.OnKeyDown( self, event ) def OnExecute( self, event ): str = clip_list.GetText()[clip_list.GetCursor()] OpenClipboard() EmptyClipboard() try: SetClipboardText(str) except: SetValue('SetValue Error') CloseClipboard() clip_list.add(clip_list.pop(clip_list.GetCursor())) clip_list.SetCursor() self.SetClipmode(CLIPMODE_NORMAL) import clcore clcore.RaiseNextWindow() event.Skip() def OnAbbrev( self, event ): event.Skip() def OnListSelChange( self, event ): import clwindow clip_list.SetCursor(event.sel) clwindow.edit.SelChange( event.str ) def PopMode(self): RemoveTimerHandler( self.OnTimer ) PopMode() def SetClipmode(self, activemode = CLIPMODE_NORMAL): self.clipmode_active = activemode if self.clipmode_active == CLIPMODE_NORMAL: SetStatusIndicator('@-') AddTimerHandler( timer_interval, self.OnTimer ) PopList() else : SetStatusIndicator('@>') RemoveTimerHandler( self.OnTimer ) def Shortcut_ClipMode(): import clmode cm = clmode.mode_stack[0] try: cm.SetClipmode(CLIPMODE_ACTIVE) except: cm = ClipMode(CLIPMODE_ACTIVE) PushMode( cm ) PopList(clip_list.GetTitle()) SetValue(clip_list.GetTitle()[0]) SetForegroundWindow( GetHandle() )
Config.py側の設定は以下の通り。
import clcore from clmode import BaseMode def MyOnActivate( self, event ): BaseMode.OnActivate( self, event ) if event.active: active_bg_color = GetOption( OPTION_ACTIVE_BG_COLOR ) SetBgColor( *active_bg_color ) else: inactive_bg_color = GetOption( OPTION_INACTIVE_BG_COLOR ) SetBgColor( *inactive_bg_color ) if not event.active and len(GetValue())==0 : original_pos = GetOption( OPTION_ORIGINAL_POSITION ) SetPos( *original_pos ) PopMode() #import clmode_clock #PushMode( clmode_clock.ClockMode() ) import clmode_clip PushMode( clmode_clip.ClipMode() ) SetImeStatus(False) from clmode_launcher import LauncherMode LauncherMode.OnActivate = MyOnActivate import clmode_clip AddHotKeyHandler( ord('L'), MODKEY_CTRL | MODKEY_ALT, clmode_clip.Shortcut_ClipMode )
本当はちゃんとクリップボードチェーンに追加してメッセージベースでクリップボード監視をしたかったのだが、メッセージをどうしても受け取れないのでタイマーでお茶を濁した。エディットのサブクラス化はさすがに無理をしすぎなのか。タイマーの間隔をあまり小さくするとシステムに負荷がかかるし、あまり大きくするとクリップボードの内容を取りこぼす可能性が高くなる。使用者が各自で程よい設定を模索してもらいたい。
あんまり大きなデータをコピーすると死ぬかもしれないし死なないかもしれない。今のところ死んで無いが、しばらく使ってみて自分が気になるようならOnTimerでクリップボードの文字列を取得する際に何とかしてみようかと思った。
履歴モード時に、BackSpaceやDeleteでリストが消えないように手を入れてみた。ランチャーモードにも応用できそうだが個人的にはそれほど必要としていないので今回は見送った。すごい寝ぼけたコードだったので一時コメント化。恥ずかしいがとりあえず残しておく。
config.pyはいろいろ編集したのでいい加減訳がわからなくなってきた。そろそろ整理しようと思う。