fzeet 0.6.1 → 0.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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