1#!/usr/bin/env kross
2
3# Load the Qt4-QtRuby module.
4begin
5    require 'Qt'
6rescue LoadError
7    raise "Failed to load the required QtRuby module. Please install Qt4-QtRuby."
8end
9
10# Load the Words scripting module.
11begin
12    require 'Words'
13rescue LoadError
14    # If loading failed (cause the script was not executed from within
15    # a running Words instance) we try to load the Kross module and
16    # ask it to load the Words module for us.
17    require 'Kross'
18    Words = Kross.module('Words')
19
20    # Testcase to fill Words with some content. You are also able to e.g.
21    # load a document here with "Words.document().openUrl('/path/file.odt')"
22    mytextframeset1 = Words.addTextFrameSet("myFirstTextFrame")
23    mytextframeset1.addTextFrame()
24    mytextframeset1.document().setHtml("<h1>First Header</h1><p>Some text</p><p>Some more text with <b>bold</b> and <i>italic</i> and <u>underline</u> to test the stuff.</p>")
25    mytextframeset2 = Words.addTextFrameSet("mySecondTextFrame")
26    mytextframeset2.addTextFrame()
27    mytextframeset2.document().setHtml("<h1>Second Header</h1><h2>First Sub Header</h2><p>Some text.</p><h2>Second Sub Header</h2><p>Some text.</p>")
28end
29
30#########################################################################
31# Pages - Each item is able to provide different pages
32
33# The Page interface class.
34class Page
35    def initialize(label)
36        @label = label
37    end
38    def label
39        return @label
40    end
41    def createWidget(parent)
42        return nil
43    end
44end
45
46# The PropertyPage class implements properties displayed within a table.
47class PropertyPage < Page
48
49    class Property
50        def initialize(name, value)
51            @name = name
52            @value = value
53        end
54        def name
55            return @name.to_s
56        end
57        def value
58            return @value.to_s
59        end
60    end
61
62    def initialize(label, properties = [])
63        super(label)
64        @properties = properties
65    end
66
67    def createWidget(parent)
68        table = Qt::TableWidget.new(parent)
69        table.setColumnCount(2)
70        table.setHorizontalHeaderLabels( ["Name","Value"] )
71        table.verticalHeader().hide
72        table.alternatingRowColors = true
73        #table.horizontalHeader().resizeSections( Qt::HeaderView::ResizeToContents )
74        table.horizontalHeader().setStretchLastSection(true)
75        #table.setEditTriggers( Qt::AbstractItemView::AllEditTriggers )
76        table.setEditTriggers( Qt::AbstractItemView::NoEditTriggers )
77        #@table.setSortingEnabled(true)
78        for property in @properties
79            rows = table.rowCount
80            table.insertRow(rows)
81            table.setItem(rows, 0, Qt::TableWidgetItem.new( property.name ))
82            table.setItem(rows, 1, Qt::TableWidgetItem.new( property.value ))
83        end
84        return table
85    end
86
87    def addProperty(property)
88        @properties.push(property)
89    end
90
91end
92
93# The TextEditPage class implements a simple texteditor.
94class TextEditPage < Page
95    def initialize(label, text = "")
96        super(label)
97        @text = text
98    end
99    def createWidget(parent)
100        edit = Qt::TextEdit.new(parent)
101        if @text
102            edit.plainText = @text.to_s
103        end
104        edit.readOnly = true
105        return edit
106    end
107end
108
109# The TextBrowserPage class implements a simple browser.
110class TextBrowserPage < Page
111    def initialize(label, text = "")
112        super(label)
113        @text = text
114    end
115    def createWidget(parent)
116        edit = Qt::TextBrowser.new(parent)
117        if @text
118            edit.html = @text.to_s
119        end
120        edit.readOnly = true
121        return edit
122    end
123end
124
125#########################################################################
126# Items displayed within the TreeModel.
127
128# Item is the interface class for all items.
129class Item
130
131    def initialize(parentitem = nil, data = nil)
132        @parentitem = parentitem
133        @data = data
134        @childitems = []
135        @pages = []
136    end
137
138    def parent
139        return @parentitem
140    end
141
142    def hasChildren
143        return @childitems.length > 0
144    end
145
146    def child(row)
147        return @childitems[row]
148    end
149
150    def childCount
151        return @childitems.length
152    end
153
154    def row
155        #if !@parentitem.nil?
156        #    return @parentitem.childitems.index(self)
157        #end
158        return 0
159    end
160
161    def page(index)
162        return @pages[index]
163    end
164
165    def pageCount
166        return @pages.length
167    end
168
169end
170
171# FrameItem for Words Frame objects.
172class FrameItem < Item
173
174    class TextFrameItem < Item
175        def initialize(parentitem, textframe)
176            super(parentitem, textframe)
177            #cursor = textframe.firstCursorPosition()
178            for i in 0..(textframe.childFrameCount() - 1)
179                f = textframe.childFrame(i)
180                if f
181                    puts "TextFrameItem %s" % f.to_s
182                    @childitems.push( TextFrameItem.new(self, f) )
183                end
184            end
185            if @data.text
186                @pages.push( TextEditPage.new("Text", @data.text) )
187            end
188        end
189        def data(column)
190            #return Qt::Variant.new( "TextFrame %i-%i" % [@data.firstPosition(),@data.lastPosition()] )
191            return Qt::Variant.new( "TextFrame" )
192        end
193    end
194
195    class TextListItem < Item
196        def initialize(parentitem, textlist)
197            super(parentitem, textlist)
198            if @data.text
199                @pages.push( TextEditPage.new("Text", @data.text) )
200            end
201        end
202        def data(column)
203            return Qt::Variant.new( "TextList" )
204        end
205    end
206
207    class TextTableItem < Item
208        def initialize(parentitem, texttable)
209            super(parentitem, texttable)
210            if @data.text
211                @pages.push( TextEditPage.new("Text", @data.text) )
212            end
213        end
214        def data(column)
215            return Qt::Variant.new( "TextTable" )
216        end
217    end
218
219    class TextDocumentItem < Item
220        def initialize(frameitem, textdoc)
221            super(frameitem, textdoc)
222
223            @pages.push(
224                PropertyPage.new("Properties", [
225                    PropertyPage::Property.new("width", @data.width),
226                    PropertyPage::Property.new("height", @data.height),
227                    PropertyPage::Property.new("pageCount", @data.pageCount),
228                    PropertyPage::Property.new("isModified", @data.isModified),
229                ] )
230            )
231
232            varpage = PropertyPage.new("Variables")
233            for n in @data.variableNames
234                varpage.addProperty( PropertyPage::Property.new(n, @data.variableValue(n)) )
235            end
236            @pages.push(varpage)
237
238            @pages.push( TextEditPage.new("Text", @data.toText) )
239            @pages.push( TextEditPage.new("HTML", @data.toHtml) )
240            @pages.push( TextEditPage.new("Style", @data.defaultStyleSheet) )
241            @pages.push( TextBrowserPage.new("Preview", @data.toHtml) )
242
243            @rootframe = textdoc.rootFrame
244        end
245
246        def hasChildren
247            return true
248        end
249        def child(row)
250            if @childitems.length <= 0
251                lazyLoadChildren()
252            end
253            return @childitems[row]
254        end
255        def childCount
256            if @childitems.length <= 0
257                lazyLoadChildren()
258            end
259            return @childitems.length
260        end
261        def data(column)
262            return Qt::Variant.new("Document")
263        end
264
265        def lazyLoadChildren
266            textcursor = @rootframe.firstCursorPosition()
267            addCurrentBlock(textcursor)
268            while textcursor.movePosition( textcursor.NextBlock )
269                addCurrentBlock(textcursor)
270            end
271        end
272        def addCurrentBlock(textcursor)
273            if textcursor.currentTable()
274                @childitems.push( TextTableItem.new(self, textcursor.currentTable()) )
275            elsif textcursor.currentList()
276                @childitems.push( TextListItem.new(self, textcursor.currentList()) )
277            elsif textcursor.currentFrame()
278                @childitems.push( TextFrameItem.new(self, textcursor.currentFrame()) )
279            end
280        end
281    end
282
283    def initialize(framesetitem, frame)
284        super(framesetitem, frame)
285        frameset = frame.frameSet
286        textdoc = frameset.document
287        if textdoc != 0
288            @childitems.push( TextDocumentItem.new(self, textdoc) )
289        end
290        @pages.push(
291            PropertyPage.new("Properties", [
292                PropertyPage::Property.new("width", @data.width),
293                PropertyPage::Property.new("shapeId", @data.shapeId),
294                PropertyPage::Property.new("visible", @data.isVisible),
295                PropertyPage::Property.new("rotation", @data.rotation),
296                PropertyPage::Property.new("width", @data.width),
297                PropertyPage::Property.new("height", @data.height),
298                PropertyPage::Property.new("positionX", @data.positionX),
299                PropertyPage::Property.new("positionY", @data.positionY),
300                PropertyPage::Property.new("zIndex", @data.zIndex),
301            ] )
302        )
303    end
304
305    def data(column)
306        return Qt::Variant.new( @data.shapeId().to_s )
307    end
308
309end
310
311# FramesetItem for Words FrameSet objects.
312class FramesetItem < Item
313
314    def initialize(rootitem, frameset)
315        super(rootitem, frameset)
316        for i in 0..(frameset.frameCount() - 1)
317            f = frameset.frame(i)
318            if f
319                puts "FramesetItem frame=%s" % f.to_s
320                @childitems.push( FrameItem.new(self, f) )
321            end
322        end
323        @pages.push(
324            PropertyPage.new("Properties", [
325                PropertyPage::Property.new("name", @data.name),
326                PropertyPage::Property.new("frameCount", @data.frameCount),
327                PropertyPage::Property.new("isText", @data.document != 0),
328            ] )
329        )
330
331    end
332
333    def data(column)
334        return Qt::Variant.new( @data.name().to_s )
335    end
336
337end
338
339# Rootitem within the TreeModel.
340class RootItem < Item
341
342    def initialize
343        super(nil, nil)
344        for i in 0..(Words.frameSetCount() - 1)
345            f = Words.frameSet(i)
346            if f
347                puts "RootItem %s" % f.to_s
348                @childitems.push( FramesetItem.new(self, f) )
349            end
350        end
351    end
352
353    def data(column)
354        return Qt::Variant.new
355    end
356
357end
358
359#########################################################################
360# TreeModel - The model that handles the items.
361
362class TreeModel < Qt::AbstractItemModel
363
364    def initialize(parent)
365        super(parent)
366        @rootItem = RootItem.new
367    end
368
369    def columnCount(parent)
370        return 1
371    end
372
373    def flags(index)
374        if !index.valid?
375            return Qt::ItemIsEnabled
376        end
377        return Qt::ItemIsEnabled | Qt::ItemIsSelectable
378    end
379
380    def index(row, column, parent)
381        if !parent.valid?
382            parentItem = @rootItem
383        else
384            parentItem = parent.internalPointer
385        end
386        childItem = parentItem.child(row)
387        if !childItem.nil?
388            return createIndex(row, column, childItem)
389        end
390        return Qt::ModelIndex.new
391    end
392
393    def parent(index)
394        if index.valid?
395            parentItem = index.internalPointer.parent
396            if parentItem != @rootItem
397                return createIndex(parentItem.row, 0, parentItem)
398            end
399        end
400        return Qt::ModelIndex.new
401    end
402
403    def data(index, role)
404        if !index.valid?
405            return Qt::Variant.new
406        end
407        if role == Qt::DisplayRole
408            item = index.internalPointer
409            return item.data(index.column)
410        end
411        return Qt::Variant.new
412    end
413
414    def rowCount(parent)
415        if !parent.valid?
416            parentItem = @rootItem
417        else
418            parentItem = parent.internalPointer
419        end
420        return parentItem.childCount
421    end
422
423    def hasChildren(parent)
424        if !parent.valid?
425            parentItem = @rootItem
426        else
427            parentItem = parent.internalPointer
428        end
429        return parentItem.hasChildren
430    end
431
432end
433
434#########################################################################
435# Dialog - The main dialog window.
436
437class Dialog < Qt::Dialog
438
439    slots 'slotCurrentChanged(const QModelIndex&, const QModelIndex&)'
440
441    def recursiveExpand(level, modelindex)
442        level += 1
443        for i in 0..(@view.model.rowCount(modelindex) - 1)
444            idx = @view.model().index(i,0,modelindex)
445            if level <= 2
446                @view.expand( idx )
447                recursiveExpand(level, idx)
448            end
449        end
450    end
451
452    def initialize
453        super()
454        self.windowTitle = 'Document Tree'
455
456        layout = Qt::HBoxLayout.new
457        setLayout(layout)
458        splitter = Qt::Splitter.new(self)
459        layout.addWidget(splitter)
460
461        @view = Qt::TreeView.new(self)
462        @view.header().setVisible(false)
463        @view.model = TreeModel.new(self)
464        @view.selectionModel = Qt::ItemSelectionModel.new(@view.model)
465        recursiveExpand(0, Qt::ModelIndex.new)
466        #@view.expandAll
467        @view.show
468        splitter.addWidget(@view)
469
470        editorwidget = Qt::Widget.new(self)
471        editorlayout = Qt::VBoxLayout.new
472        editorlayout.setMargin(0)
473        editorwidget.setLayout(editorlayout)
474        #@label = Qt::Label.new(editorwidget)
475        #editorlayout.addWidget(@label)
476        @tab = Qt::TabWidget.new(self)
477        editorlayout.addWidget(@tab)
478        #layout.addSpacing(10)
479        #layout.addStretch(1)
480        splitter.addWidget(editorwidget)
481        editorwidget.resize( Qt::Size.new(360, 400).expandedTo( editorwidget.minimumSizeHint ) )
482
483        connect(@view.selectionModel, SIGNAL('currentChanged(const QModelIndex&, const QModelIndex&)'), self, SLOT('slotCurrentChanged(const QModelIndex&, const QModelIndex&)'))
484        resize( Qt::Size.new(700, 520).expandedTo( minimumSizeHint ) );
485    end
486
487    def slotCurrentChanged(current, prev)
488        while @tab.count > 0; @tab.removeTab(0); end
489        if current.valid?
490            #@label.text = @view.model().data(current, Qt::DisplayRole)
491            for i in 0..(current.internalPointer.pageCount() - 1)
492                page = current.internalPointer.page(i)
493                widget = page.createWidget(@tab)
494                if widget != nil
495                    @tab.addTab(widget, page.label)
496                end
497            end
498        end
499    end
500
501end
502
503dialog = Dialog.new
504dialog.exec
505