fzeet 0.6.1 → 0.6.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. data/examples/UIRibbon/ApplicationModes.dll +0 -0
  2. data/examples/UIRibbon/ApplicationModes.rb +40 -0
  3. data/examples/UIRibbon/ApplicationModes.rbw +14 -0
  4. data/examples/UIRibbon/ApplicationModes.xml +83 -0
  5. data/examples/UIRibbon/Color.rbw +1 -1
  6. data/examples/UIRibbon/Control/Button.dll +0 -0
  7. data/examples/UIRibbon/Control/Button.rb +9 -5
  8. data/examples/UIRibbon/Control/Button.rbw +3 -1
  9. data/examples/UIRibbon/Control/Button.xml +6 -0
  10. data/examples/UIRibbon/Control/ColorPicker.dll +0 -0
  11. data/examples/UIRibbon/Control/ColorPicker.rb +55 -0
  12. data/examples/UIRibbon/Control/ColorPicker.rbw +91 -0
  13. data/examples/UIRibbon/Control/ColorPicker.xml +131 -0
  14. data/examples/UIRibbon/Control/ComboBox.dll +0 -0
  15. data/examples/UIRibbon/Control/ComboBox.rb +23 -0
  16. data/examples/UIRibbon/Control/ComboBox.rbw +56 -0
  17. data/examples/UIRibbon/Control/ComboBox.xml +75 -0
  18. data/examples/UIRibbon/Control/FontControl.dll +0 -0
  19. data/examples/UIRibbon/Control/FontControl.rb +34 -0
  20. data/examples/UIRibbon/Control/FontControl.rbw +83 -0
  21. data/examples/UIRibbon/Control/FontControl.xml +96 -0
  22. data/examples/UIRibbon/Control/Gallery.dll +0 -0
  23. data/examples/UIRibbon/Control/Gallery.rb +55 -0
  24. data/examples/UIRibbon/Control/Gallery.rbw +124 -0
  25. data/examples/UIRibbon/Control/Gallery.xml +132 -0
  26. data/examples/UIRibbon/Control/Spinner.dll +0 -0
  27. data/examples/UIRibbon/Control/Spinner.rb +14 -0
  28. data/examples/UIRibbon/Control/Spinner.rbw +27 -0
  29. data/examples/UIRibbon/Control/Spinner.xml +37 -0
  30. data/examples/UIRibbon/Size.rbw +1 -1
  31. data/examples/UIRibbon/TabGroup.dll +0 -0
  32. data/examples/UIRibbon/TabGroup.rb +12 -0
  33. data/examples/UIRibbon/TabGroup.rbw +23 -0
  34. data/examples/UIRibbon/TabGroup.xml +42 -0
  35. data/examples/UIRibbon/Viewer.rbw +1 -1
  36. data/examples/res/applications-graphics-big.bmp +0 -0
  37. data/examples/res/applications-graphics-small.bmp +0 -0
  38. data/examples/res/applications-graphics.bmp +0 -0
  39. data/examples/res/document-print-big.bmp +0 -0
  40. data/examples/res/document-print-preview-big.bmp +0 -0
  41. data/examples/res/document-print-preview-small.bmp +0 -0
  42. data/examples/res/document-print-preview.bmp +0 -0
  43. data/examples/res/document-print-small.bmp +0 -0
  44. data/examples/res/document-print.bmp +0 -0
  45. data/examples/res/go-bottom-big.bmp +0 -0
  46. data/examples/res/go-bottom-small.bmp +0 -0
  47. data/examples/res/go-bottom.bmp +0 -0
  48. data/examples/res/preferences-desktop-font-big.bmp +0 -0
  49. data/examples/res/preferences-desktop-font-small.bmp +0 -0
  50. data/examples/res/preferences-desktop-font.bmp +0 -0
  51. data/examples/res/process-stop-big.bmp +0 -0
  52. data/examples/res/process-stop-small.bmp +0 -0
  53. data/examples/res/process-stop.bmp +0 -0
  54. data/lib/fzeet/Application.rb +6 -1
  55. data/lib/fzeet/Common.rb +17 -0
  56. data/lib/fzeet/Dialog/ShellFileDialog.rb +8 -48
  57. data/lib/fzeet/UIRibbon.rb +174 -7
  58. data/lib/fzeet/windows/com.rb +2 -0
  59. data/lib/fzeet/windows/common.rb +47 -6
  60. data/lib/fzeet/windows/gdi.rb +8 -0
  61. data/lib/fzeet/windows/kernel.rb +2 -0
  62. data/lib/fzeet/windows/ole.rb +34 -0
  63. data/lib/fzeet/windows/propsys.rb +7 -1
  64. data/lib/fzeet/windows/shell/Common.rb +44 -0
  65. data/lib/fzeet/windows/shlwapi.rb +10 -0
  66. data/lib/fzeet/windows/uiribbon.rb +15 -0
  67. data/lib/fzeet/windows.rb +1 -0
  68. metadata +51 -8
@@ -0,0 +1,42 @@
1
+ <?xml version='1.0' encoding='utf-8' ?>
2
+ <Application xmlns='http://schemas.microsoft.com/windows/2009/Ribbon'>
3
+ <Application.Commands>
4
+ <Command Name='cmdAppMenu' />
5
+
6
+ <Command Name='cmdQAT' />
7
+
8
+ <Command Name='cmdTab1' LabelTitle='Tab1' />
9
+
10
+ <Command Name='cmdTabGroup1' LabelTitle='TabGroup1' />
11
+
12
+ <Command Name='cmdTabGroup1Tab1' LabelTitle='Tab1' />
13
+ <Command Name='cmdTabGroup1Tab2' LabelTitle='Tab2' />
14
+ </Application.Commands>
15
+
16
+ <Application.Views>
17
+ <Ribbon>
18
+ <Ribbon.ApplicationMenu>
19
+ <ApplicationMenu CommandName='cmdAppMenu'>
20
+
21
+ </ApplicationMenu>
22
+ </Ribbon.ApplicationMenu>
23
+
24
+ <Ribbon.QuickAccessToolbar>
25
+ <QuickAccessToolbar CommandName='cmdQAT'>
26
+
27
+ </QuickAccessToolbar>
28
+ </Ribbon.QuickAccessToolbar>
29
+
30
+ <Ribbon.Tabs>
31
+ <Tab CommandName='cmdTab1' />
32
+ </Ribbon.Tabs>
33
+
34
+ <Ribbon.ContextualTabs>
35
+ <TabGroup CommandName='cmdTabGroup1'>
36
+ <Tab CommandName='cmdTabGroup1Tab1' />
37
+ <Tab CommandName='cmdTabGroup1Tab2' />
38
+ </TabGroup>
39
+ </Ribbon.ContextualTabs>
40
+ </Ribbon>
41
+ </Application.Views>
42
+ </Application>
@@ -5,5 +5,5 @@ include Fzeet
5
5
  Application.name += " - #{name = ARGV[0] || 'Minimal'}.xml"
6
6
 
7
7
  Application.run { |window|
8
- UIRibbon.new(window, name)
8
+ UIRibbon.new(window, name: name)
9
9
  }
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -12,10 +12,15 @@ module Fzeet
12
12
  @accelerators = []
13
13
  @dialogs = []
14
14
  @images = {}
15
+ Fzeet.using(ScreenDC.new) { |dc|
16
+ @devcaps = {
17
+ logpixelsy: Windows.GetDeviceCaps(dc.handle, Windows::LOGPIXELSY)
18
+ }
19
+ }
15
20
 
16
21
  class << self
17
22
  attr_accessor :name, :version, :authors, :quitWhenMainWindowCloses
18
- attr_reader :window, :accelerators, :dialogs, :images
23
+ attr_reader :window, :accelerators, :dialogs, :images, :devcaps
19
24
  end
20
25
 
21
26
  def self.window=(window)
data/lib/fzeet/Common.rb CHANGED
@@ -153,6 +153,10 @@ module Fzeet
153
153
  def rb; Point.new(self[:right], self[:bottom]) end
154
154
  end
155
155
 
156
+ class LOGFONT
157
+ include Toggle
158
+ end
159
+
156
160
  class Message < Windows::MSG
157
161
  def get!(window = nil, msgFilterMin = 0, msgFilterMax = 0)
158
162
  Windows.DetonateLastError(-1, :GetMessage, self, window && window.handle, msgFilterMin, msgFilterMax) != 0
@@ -299,6 +303,9 @@ module Fzeet
299
303
  def color; Windows.GetTextColor(@handle) end
300
304
  def color=(color) Windows.SetTextColor(@handle, color) end
301
305
 
306
+ def background; Windows.GetBkColor(@handle) end
307
+ def background=(background) Windows.SetBkColor(@handle, background) end
308
+
302
309
  def fillRect(rect, brush) Windows.DetonateLastError(0, :FillRect, @handle, rect, brush.handle); self end
303
310
 
304
311
  def text(text, rect, flags = 0) Windows.DetonateLastError(0, :DrawText, @handle, text, -1, rect, Fzeet.flags(flags, :dt_)); self end
@@ -328,4 +335,14 @@ module Fzeet
328
335
 
329
336
  def dispose; Windows.ReleaseDC(@window.handle, @handle); detach end
330
337
  end
338
+
339
+ class ScreenDC < Handle
340
+ include DCMethods
341
+
342
+ def initialize
343
+ @handle = Windows.DetonateLastError(FFI::Pointer::NULL, :GetDC, nil); attach
344
+ end
345
+
346
+ def dispose; Windows.ReleaseDC(nil, @handle); detach end
347
+ end
331
348
  end
@@ -10,78 +10,38 @@ module Fzeet
10
10
  result
11
11
  end
12
12
 
13
- def path
14
- result = nil
15
-
13
+ def item
16
14
  FFI::MemoryPointer.new(:pointer) { |psi|
17
15
  next unless GetResult(psi) == Windows::S_OK
18
16
 
19
17
  si = Windows::ShellItem.new(psi.read_pointer)
20
18
 
21
19
  begin
22
- FFI::MemoryPointer.new(:pointer) { |pwcs|
23
- next unless si.GetDisplayName(Windows::SIGDN_FILESYSPATH, pwcs) == Windows::S_OK
24
-
25
- wcs = pwcs.read_pointer
26
-
27
- begin
28
- result = Windows.WCSTOMBS(wcs)
29
- ensure
30
- Windows.CoTaskMemFree(wcs)
31
- end
32
- }
20
+ yield si
33
21
  ensure
34
22
  si.Release
35
23
  end
36
24
  }
37
-
38
- result
39
25
  end
40
26
 
41
- def paths
42
- result = []
43
-
27
+ def items
44
28
  FFI::MemoryPointer.new(:pointer) { |psia|
45
29
  next unless GetResults(psia) == Windows::S_OK
46
30
 
47
31
  sia = Windows::ShellItemArray.new(psia.read_pointer)
48
32
 
49
33
  begin
50
- FFI::MemoryPointer.new(:ulong) { |pc|
51
- next unless sia.GetCount(pc) == Windows::S_OK
52
-
53
- pc.read_long.times { |i|
54
- FFI::MemoryPointer.new(:pointer) { |psi|
55
- next unless sia.GetItemAt(i, psi) == Windows::S_OK
56
-
57
- si = Windows::ShellItem.new(psi.read_pointer)
58
-
59
- begin
60
- FFI::MemoryPointer.new(:pointer) { |pwcs|
61
- next unless si.GetDisplayName(Windows::SIGDN_FILESYSPATH, pwcs) == Windows::S_OK
62
-
63
- wcs = pwcs.read_pointer
64
-
65
- begin
66
- result << Windows.WCSTOMBS(wcs)
67
- ensure
68
- Windows.CoTaskMemFree(wcs)
69
- end
70
- }
71
- ensure
72
- si.Release
73
- end
74
- }
75
- }
76
- }
34
+ yield sia
77
35
  ensure
78
36
  sia.Release
79
37
  end
80
38
  }
81
-
82
- result
83
39
  end
84
40
 
41
+ def path; item { |si| return si.path } end
42
+
43
+ def paths; items { |sia| return sia.map(&:path) } end
44
+
85
45
  def show(window = Application.window)
86
46
  DialogResult.new((Show(window.handle) == Windows::S_OK) ? Windows::IDOK : Windows::IDCANCEL)
87
47
  end
@@ -7,6 +7,38 @@ module Fzeet
7
7
 
8
8
  prop(*args)
9
9
  end
10
+
11
+ def update(from)
12
+ case from
13
+ when Windows::LOGFONT
14
+ uiprop(:FontProperties_Family, Windows::PROPVARIANT[:wstring, from.face])
15
+ uiprop(:FontProperties_Size, Windows::PROPVARIANT[:decimal, from.size.round])
16
+ uiprop(:FontProperties_Bold, Windows::PROPVARIANT[:uint, (from.bold?) ? 2 : 1])
17
+ uiprop(:FontProperties_Italic, Windows::PROPVARIANT[:uint, (from.italic?) ? 2 : 1])
18
+ uiprop(:FontProperties_Underline, Windows::PROPVARIANT[:uint, (from.underline?) ? 2 : 1])
19
+ uiprop(:FontProperties_Strikethrough, Windows::PROPVARIANT[:uint, (from.strikeout?) ? 2 : 1])
20
+ else raise ArgumentError
21
+ end
22
+
23
+ self
24
+ end
25
+ end
26
+
27
+ class Windows::UISimplePropertySet
28
+ def get(k) Windows::PROPVARIANT.new.tap { |v| GetValue(k, v) } end
29
+
30
+ def prop(*args)
31
+ case args.length
32
+ when 1; get(*args)
33
+ else raise ArgumentError
34
+ end
35
+ end
36
+
37
+ def uiprop(*args)
38
+ args[0] = Windows.const_get("UI_PKEY_#{args[0]}")
39
+
40
+ prop(*args)
41
+ end
10
42
  end
11
43
 
12
44
  class UIRibbon
@@ -102,11 +134,63 @@ module Fzeet
102
134
  }
103
135
  end
104
136
 
105
- def initialize(_window, name = Application.name, resname = 'APPLICATION_RIBBON')
137
+ class GalleryItem < Windows::UISimplePropertySetCallback
138
+ def initialize(label, categoryId = 0, image = nil)
139
+ @label, @categoryId, @image = label, categoryId, image
140
+
141
+ super()
142
+ end
143
+
144
+ attr_reader :label, :categoryId, :image
145
+
146
+ def GetValue(pkey, pvalue)
147
+ key, value = Windows::PROPERTYKEY.new(pkey), Windows::PROPVARIANT.new(pvalue)
148
+
149
+ case key
150
+ when Windows::UI_PKEY_CategoryId
151
+ value.uint = @categoryId; Windows::S_OK
152
+ when Windows::UI_PKEY_Label
153
+ value.wstring = @label; Windows::S_OK
154
+ when Windows::UI_PKEY_ItemImage
155
+ FFI::MemoryPointer.new(:pointer) { |punk|
156
+ Windows::UIImageFromBitmap.new.CreateImage(PARGB32.new(@image).handle, Windows::UI_OWNERSHIP_TRANSFER, punk)
157
+
158
+ unk = Windows::Unknown.new(punk.read_pointer)
159
+ value.unknown = unk
160
+ unk.Release
161
+ }
162
+
163
+ Windows::S_OK
164
+ else
165
+ Windows::E_NOTIMPL
166
+ end
167
+ end
168
+ end
169
+
170
+ def initialize(_window, opts = {})
171
+ handlers = {}
172
+
173
+ opts.delete_if { |k, v|
174
+ next false unless v.kind_of?(Proc)
175
+
176
+ handlers[k] = v; true
177
+ }
178
+
179
+ _opts = {
180
+ name: Application.name,
181
+ resname: 'APPLICATION_RIBBON'
182
+ }
183
+ badopts = opts.keys - _opts.keys; raise "Bad option(s): #{badopts.join(', ')}." unless badopts.empty?
184
+ _opts.merge!(opts)
185
+
106
186
  @creates = []
107
187
  @destroys = []
108
188
  @sizes = []
109
189
  @executesAllVerbs = {}
190
+ @executesExecute = {}
191
+ @executesPreview = {}
192
+ @executesCancelPreview = {}
193
+ @updatesAllKeys = {}
110
194
 
111
195
  @window = _window
112
196
 
@@ -124,6 +208,7 @@ module Fzeet
124
208
  attr_reader :ribbon
125
209
 
126
210
  def Execute(*args) ribbon.execute(*args); Windows::S_OK end
211
+ def UpdateProperty(*args) ribbon.update(*args); Windows::S_OK end
127
212
  end
128
213
 
129
214
  @uia = Windows::UIApplication.new
@@ -183,10 +268,18 @@ module Fzeet
183
268
  def OnCreateUICommand(*args) uich.QueryInterface(uich.class::IID, args[-1]); Windows::S_OK end
184
269
  end
185
270
 
271
+ @hdll = Windows.LoadRibbonDll(_opts[:name])
272
+
273
+ handlers.each { |k, v|
274
+ k[1] = Object.const_get(k[1]) if k.length > 1
275
+
276
+ on(*k, &v)
277
+ }
278
+
186
279
  @uif = Windows::UIFramework.new
187
280
 
188
281
  uif.Initialize(window.handle, uia)
189
- uif.LoadUI(@hdll = Windows.LoadRibbonDll(name), "#{resname}\0".encode('utf-16le'))
282
+ uif.LoadUI(@hdll, "#{_opts[:resname]}\0".encode('utf-16le'))
190
283
 
191
284
  window.on(:destroy) {
192
285
  uich.Release
@@ -203,15 +296,28 @@ module Fzeet
203
296
  args = {
204
297
  commandId: commandId,
205
298
  verb: verb,
206
- key: key,
207
- value: value,
208
- props: props,
299
+ key: (key.null?) ? nil : Windows::PROPERTYKEY.new(key),
300
+ value: (value.null?) ? nil : Windows::PROPVARIANT.new(value),
301
+ props: (props.null?) ? nil : Windows::UISimplePropertySet.new(props),
209
302
  ribbon: self,
210
303
  sender: Command.new(self, commandId)
211
304
  }
212
305
 
213
- if @executesAllVerbs.include?(commandId)
214
- @executesAllVerbs[commandId].each { |handler|
306
+ (handlers = @executesAllVerbs[commandId]) and handlers.each { |handler|
307
+ (handler.arity == 0) ? handler.call : handler.call(args)
308
+ }
309
+
310
+ case verb
311
+ when Windows::UI_EXECUTIONVERB_EXECUTE
312
+ (handlers = @executesExecute[commandId]) and handlers.each { |handler|
313
+ (handler.arity == 0) ? handler.call : handler.call(args)
314
+ }
315
+ when Windows::UI_EXECUTIONVERB_PREVIEW
316
+ (handlers = @executesPreview[commandId]) and handlers.each { |handler|
317
+ (handler.arity == 0) ? handler.call : handler.call(args)
318
+ }
319
+ when Windows::UI_EXECUTIONVERB_CANCELPREVIEW
320
+ (handlers = @executesCancelPreview[commandId]) and handlers.each { |handler|
215
321
  (handler.arity == 0) ? handler.call : handler.call(args)
216
322
  }
217
323
  end
@@ -223,6 +329,33 @@ module Fzeet
223
329
  self
224
330
  end
225
331
 
332
+ def update(commandId, key, value, newValue)
333
+ args = {
334
+ commandId: commandId,
335
+ key: (key.null?) ? nil : Windows::PROPERTYKEY.new(key),
336
+ value: (value.null?) ? nil : Windows::PROPVARIANT.new(value),
337
+ newValue: (newValue.null?) ? nil : Windows::PROPVARIANT.new(newValue),
338
+ ribbon: self,
339
+ sender: Command.new(self, commandId)
340
+ }
341
+
342
+ (handlers = @updatesAllKeys[commandId]) and handlers.each { |handler|
343
+ (handler.arity == 0) ? handler.call : handler.call(args)
344
+ }
345
+
346
+ self
347
+ rescue
348
+ Fzeet.message %Q{#{$!}\n\n#{$!.backtrace.join("\n")}}, icon: :error
349
+
350
+ self
351
+ end
352
+
353
+ def invalidate(commandId, flags = Windows::UI_INVALIDATIONS_ALLPROPERTIES, key = nil)
354
+ @uif.InvalidateUICommand(commandId, flags, key)
355
+
356
+ self
357
+ end
358
+
226
359
  def on(*args, &block)
227
360
  case args.size
228
361
  when 1
@@ -233,12 +366,46 @@ module Fzeet
233
366
  when Integer; (@executesAllVerbs[args[0]] ||= []) << block
234
367
  else raise ArgumentError
235
368
  end
369
+ when 2
370
+ case args[0]
371
+ when /^execute$/i; (@executesExecute[args[1]] ||= []) << block
372
+ when /^preview$/i; (@executesPreview[args[1]] ||= []) << block
373
+ when /^cancelpreview$/i; (@executesCancelPreview[args[1]] ||= []) << block
374
+ when /^update$/i; (@updatesAllKeys[args[1]] ||= []) << block
375
+ else raise ArgumentError
376
+ end
236
377
  else raise ArgumentError
237
378
  end
238
379
 
239
380
  self
240
381
  end
241
382
 
383
+ def fontPropsUpdate(args)
384
+ return self unless args[:key] == Windows::UI_PKEY_FontProperties
385
+
386
+ args[:value].unknown { |current|
387
+ current.QueryInstance(Windows::PropertyStore) { |ps|
388
+ yield ps
389
+
390
+ args[:newValue].unknown = current
391
+ }
392
+ }
393
+
394
+ self
395
+ end
396
+
397
+ def fontPropsChanged(args)
398
+ return self unless args[:key] == Windows::UI_PKEY_FontProperties
399
+
400
+ args[:props].uiprop(:FontProperties_ChangedProperties).unknown { |changed|
401
+ changed.QueryInstance(Windows::PropertyStore) { |ps|
402
+ yield ps
403
+ }
404
+ }
405
+
406
+ self
407
+ end
408
+
242
409
  def contextualUI(id, x, y)
243
410
  uif.UseInstance(Windows::UIContextualUI, :GetView, id) { |view|
244
411
  view.ShowAtLocation(x, y)
@@ -64,6 +64,8 @@ module Fzeet
64
64
  }
65
65
  }
66
66
  end
67
+
68
+ def ==(other) Windows.memcmp(other, self, size) == 0 end
67
69
  end
68
70
 
69
71
  CLSCTX_INPROC_SERVER = 0x1
@@ -87,13 +87,17 @@ module Fzeet
87
87
  def GET_Y_LPARAM(l) ((w = HIWORD(l)) > 0x7fff) ? w - 0x1_0000 : w end
88
88
 
89
89
  def LPWSTR(s)
90
- FFI::MemoryPointer.new(:uchar, (s.length + 2) * 2) { |p|
91
- "#{s}\0".encode('utf-16le').each_byte.with_index { |b, i|
92
- p.put_char(i, b)
90
+ p = FFI::MemoryPointer.new(:uchar, (s.length + 2) * 2)
93
91
 
94
- yield p
95
- }
96
- }
92
+ "#{s}\0".encode('utf-16le').each_byte.with_index { |b, i| p.put_char(i, b) }
93
+
94
+ begin
95
+ yield p; return nil
96
+ ensure
97
+ p.free
98
+ end if block_given?
99
+
100
+ p
97
101
  end
98
102
 
99
103
  def WCSTOMBS(p)
@@ -172,6 +176,43 @@ module Fzeet
172
176
  :lfQuality, :uchar,
173
177
  :lfPitchAndFamily, :uchar,
174
178
  :lfFaceName, [:char, 32]
179
+
180
+ def face; self[:lfFaceName].to_s end
181
+ def face=(face) self[:lfFaceName] = face end
182
+
183
+ def size; -Rational(self[:lfHeight] * 72, Application.devcaps[:logpixelsy]) end
184
+ def size=(size) self[:lfHeight] = -Windows.MulDiv((size * 10).to_i, Application.devcaps[:logpixelsy], 720) end
185
+
186
+ def bold?; self[:lfWeight] >= 700 end
187
+ def bold=(bold) self[:lfWeight] = (bold) ? 700 : 0 end
188
+
189
+ def italic?; self[:lfItalic] == 1 end
190
+ def italic=(italic) self[:lfItalic] = (italic) ? 1 : 0 end
191
+
192
+ def underline?; self[:lfUnderline] == 1 end
193
+ def underline=(underline) self[:lfUnderline] = (underline) ? 1 : 0 end
194
+
195
+ def strikeout?; self[:lfStrikeOut] == 1 end
196
+ def strikeout=(strikeout) self[:lfStrikeOut] = (strikeout) ? 1 : 0 end
197
+
198
+ def update(from)
199
+ case from
200
+ when PropertyStore
201
+ from.each { |k, v|
202
+ case k
203
+ when UI_PKEY_FontProperties_Family; self.face = v.wstring
204
+ when UI_PKEY_FontProperties_Size; self.size = v.decimal.to_f
205
+ when UI_PKEY_FontProperties_Bold; self.bold = v.uint == 2
206
+ when UI_PKEY_FontProperties_Italic; self.italic = v.uint == 2
207
+ when UI_PKEY_FontProperties_Underline; self.underline = v.uint == 2
208
+ when UI_PKEY_FontProperties_Strikethrough; self.strikeout = v.uint == 2
209
+ end
210
+ }
211
+ else raise ArgumentError
212
+ end
213
+
214
+ self
215
+ end
175
216
  end
176
217
 
177
218
  class SYSTEMTIME < FFI::Struct
@@ -13,6 +13,11 @@ module Fzeet
13
13
  module_function \
14
14
  :GetRValue, :GetGValue, :GetBValue, :RGB
15
15
 
16
+ LOGPIXELSX = 88
17
+ LOGPIXELSY = 90
18
+
19
+ attach_function :GetDeviceCaps, [:pointer, :int], :int
20
+
16
21
  DEFAULT_GUI_FONT = 17
17
22
 
18
23
  attach_function :GetStockObject, [:int], :pointer
@@ -46,6 +51,9 @@ module Fzeet
46
51
  attach_function :GetTextColor, [:pointer], :ulong
47
52
  attach_function :SetTextColor, [:pointer, :ulong], :ulong
48
53
 
54
+ attach_function :GetBkColor, [:pointer], :ulong
55
+ attach_function :SetBkColor, [:pointer, :ulong], :ulong
56
+
49
57
  attach_function :MoveToEx, [:pointer, :int, :int, :pointer], :int
50
58
  attach_function :LineTo, [:pointer, :int, :int], :int
51
59
  end
@@ -84,5 +84,7 @@ module Fzeet
84
84
  end
85
85
 
86
86
  module_function :EnableVisualStyles
87
+
88
+ attach_function :MulDiv, [:int, :int, :int], :int
87
89
  end
88
90
  end
@@ -85,6 +85,8 @@ module Fzeet
85
85
  key[:pid] = type
86
86
  }
87
87
  end
88
+
89
+ def ==(other) Windows.memcmp(other, self, size) == 0 end
88
90
  end
89
91
 
90
92
  class LARGE_INTEGER < FFI::Union
@@ -246,6 +248,38 @@ module Fzeet
246
248
 
247
249
  def uint; raise 'Wrong type tag.' unless self[:vt] == VT_UI4; self[:uintVal] end
248
250
  def uint=(uint) self[:vt] = VT_UI4; self[:uintVal] = uint end
251
+
252
+ def unknown
253
+ raise 'Wrong type tag.' unless self[:vt] == VT_UNKNOWN
254
+
255
+ yield Unknown.new(self[:punkVal])
256
+ ensure
257
+ Windows.PropVariantClear(self)
258
+ end
259
+
260
+ def unknown=(unknown) self[:vt] = VT_UNKNOWN; self[:punkVal] = unknown.pointer; unknown.AddRef end
261
+
262
+ def wstring; raise 'Wrong type tag.' unless self[:vt] == VT_LPWSTR; Windows.WCSTOMBS(self[:pwszVal]) end
263
+
264
+ def wstring=(string)
265
+ self[:vt] = VT_LPWSTR
266
+
267
+ FFI::MemoryPointer.new(:pointer) { |p|
268
+ Windows.DetonateHresult(:SHStrDup, string, p)
269
+
270
+ self[:pwszVal] = p.read_pointer
271
+ }
272
+ end
273
+
274
+ def decimal
275
+ raise 'Wrong type tag.' unless self[:vt] == VT_DECIMAL
276
+
277
+ Rational(self[:decVal][:Lo64], 10 ** self[:decVal][:scale]) + self[:decVal][:Hi32]
278
+ end
279
+
280
+ def decimal=(decimal) self[:vt] = VT_DECIMAL; self[:decVal][:Lo64] = decimal end
249
281
  end
282
+
283
+ attach_function :PropVariantClear, [:pointer], :long
250
284
  end
251
285
  end
@@ -18,10 +18,14 @@ module Fzeet
18
18
  PropertyStore = COM::Instance[IPropertyStore]
19
19
 
20
20
  class PropertyStore
21
- def count; count = 0; FFI::MemoryPointer.new(:ulong) { |p| count = p.get_ulong(0) }; count end
21
+ include Enumerable
22
+
23
+ def count; FFI::MemoryPointer.new(:ulong) { |pc| GetCount(pc); return pc.get_ulong(0) } end
22
24
  alias :size :count
23
25
  alias :length :count
24
26
 
27
+ def key(i) PROPERTYKEY.new.tap { |k| GetAt(i, k) } end
28
+
25
29
  # superclass.superclass is FFI::Struct, so do NOT touch [] and []=
26
30
  def get(k) PROPVARIANT.new.tap { |v| GetValue(k, v) } end
27
31
  def set(k, v) SetValue(k, v); self end
@@ -35,6 +39,8 @@ module Fzeet
35
39
  end
36
40
 
37
41
  def commit; Commit(); self end
42
+
43
+ def each; length.times { |i| v = get(k = key(i)); yield k, v }; self end
38
44
  end
39
45
  end
40
46
  end